The first half of this article introduced the big picture of programming with Java APIs--such as how Java APIs fit into application development, cloud and microservices architectures, and the role of API specs like OpenAPI. You were introduced to OpenAPI and we developed a simple example application built from an API definition.
In this article we'll continue developing our Java API definitions and application code with OpenAPI and Swagger, and we'll throw Swing Web MVC and Angular 2 into the mix. By the end of the article, we'll have used Swagger tools to both generate OpenAPI from a Spring MVC app, and generate an Angular frontend from an OpenAPI specification. You will be familiar with the core Swagger tools, and you'll know how to use them to build your own API-driven Java web apps.
Swagger and OpenAPI
As you might recall from Part 1, Swagger and OpenAPI are closely related. In fact, Swagger started out as the API definition language now known as OpenAPI. Over time, Swagger has evolved into a rich ecosystem of related projects, each one supporting the OpenAPI specification. Swagger includes a visual API designer and a handful of commercial integration products that are built on top of open source components.
Java API definitions
In Part 1, I explained the distinction between prescriptive and descriptive API use. In a prescriptive definition, the API drives code creation, whereas in a descriptive definition, we use code to generate the API. You also might see this distinction described in terms of design-first or implementation-first API usage. Both styles are valid and useful, and they are equally supported by tooling. We'll use both prescriptive and descriptive API definitions in this article.
For prescriptive API-driven development, Swagger offers the Codegen project. Codegen is able to parse an OpenAPI definition and generate code to support it, such as URL endpoint handlers and the different models defined by API paths and schemas. (See Part 1 to refresh your memory about the anatomy of an OpenAPI defintion, including the role of paths and schemas.)
The Codegen project provides a command-line interface (CLI), which is a framework for plugins supporting output to various technologies. Codegen includes a Node.js plugin that generates Node.js code, a Spring plugin for Spring-based Java, and so forth.
For descriptive API-driven development, the swagger-core
project supports interacting with OpenAPI from Java. Building on swagger-core
, the swagger-servlet
project enables outputting OpenAPI definitions from Java servlets. Other languages and stacks offer a variety of additional approaches within the Swagger ecosystem.
Swagger with Spring Web MVC
Swagger is the bridge between the OpenAPI standard and a wide range of technologies. For the purpose of this article, we'll use Swagger in a Spring Web MVC application that integrates with Spring Boot for project automation. Later in the article we'll incorporate a a frontend built with Angular 2.
In order to work with Spring, we need a project in the Swagger ecosystem that handles Spring. One of the best is SpringFox, which builds on swagger-core
to integrate Spring-based applications.
An explicit goal of SpringFox is to integrate with Spring-based apps without requiring modifications to Spring's application code or annotations. The annotations that define request mappings also drive API generation. This is a forward-looking approach, as tools become intelligent enough to understand the technology in use without intervention from the developer.
The example app
Swagger interacts with both backend and frontend code. We're going to start with the backend. We'll take a descriptive approach by creating our initial Java API definition from existing code, then we'll turn around and use a prescriptive approach to generate server-side stubs from a Java API definition.
The first thing we want to do is create a Spring-based RESTful service. Since this isn't a Spring tutorial, we'll just use an existing app: Spring Boot's Hello World example.
To obtain this project, run git clone https://github.com/spring-guides/gs-rest-service.git
on your command line. That will pull down the project. Inside you will see a /complete
directory with a simple Spring-based RESTful service.
Built with Spring Boot and Spring's Web MVC framework, this simple project consists of three classes:
Application
, used to configure the app infrastructureGreeting
, a model classGreetingController
, a resource mapping
Thanks to Spring Boot's simplified configuration, all you need to set up Spring Web MVC in Application.java
is the code in Listing 1.
Listing 1. Unmodified Application.java
@Spring BootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
All that is required to trigger Spring is the @Spring BootApplication
annotation. With Spring Web MVC already included in the dependencies, Spring Boot automatically wires the application as a Spring Web MVC project. In the main
method, a call to SpringApplication.run
starts the app, which is just a standard Java class.
The app doesn't require packaging as a WAR
file and deployment into an external container, although that is supported. Instead, the app will run as a standalone Java program that uses an embedded container; in this case Tomcat.
Running the server
Now let's run the project. At the command line, cd
to the project directory at /gs-rest-service/complete/
, and you can build and run the project with either Maven or Gradle. I'm using Maven. Simply type mvn spring-boot:run
, and the app will run with the embedded Tomcat server.
Once it's running, you can try the available endpoint in your browser; just enter: http://localhost:8080/greeting
. You'll receive a simple JSON response. You can also add a parameter, like so: http://localhost:8080/greeting?name=George Harrison
. With this parameter, you will receive a greeting for the quietest Beatle.
As I mentioned, this API is very simple, but it has the essential elements: a model suggesting an API schema, an endpoint, and parameter support. Our next step is to get an OpenAPI definition from this code.
Generating a Java API definition from code
We'll make two changes to the existing app to get our Java API definition. First, we'll modify pom.xml
to add a repository and a dependency.
In the repositories
element, add Listing 2.
Listing 2. Adding the SpringFox repository
<repository>
<id>jcenter-snapshots</id>
<name>jcenter</name>
<url>https://jcenter.bintray.com/</url>
</repository>
In the dependencies
element, add Listing 3.
Listing 3. Adding basic API support to Application.java
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.6.1</version>
</dependency>
These changes will add Swagger support to the project via SpringFox. Now you can return to Application.java
(from Listing 1) and make the modification shown in Listing 4.
Listing 4. Add basic API support to Application.java
import springfox.documentation.swagger2.annotations.EnableSwagger2;
//...
@Spring BootApplication
@EnableSwagger2
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Next, you need to create a Spring bean that will be auto-wired into the system. The Spring bean will let you configure Swagger's global settings. Add Listing 5 to the code in Application.java
.
Listing 5. Creating a SpringFox Docket bean in Application.java
@Bean
public Docket petApi() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
This is a simple configuration for the auto-wired Docket
class. SpringFox uses Docket
to configure the many parameters available in OpenAPI.
The Swagger endpoints
Now when you run the project, you will see some new endpoints are available. We are most interested in http://localhost:8080/v2/api-docs
. Check that endpoint and you will see JSON output. That is the OpenAPI definition for the example app.
Looking back at Listing 5, notice that you have the ability to filter what APIs are active. You can filter by both apis
and paths
. There are many more settings you can adjust to manipulate the API definition. For example, you could use pathMapping("/")
to establish a servlet path mapping.
Setup the Swagger UI
Enabling Swagger's UI will give you a nicer way to look at the API definition.
Return to your pom.xml
and add Listing 6 to the dependencies
.
Listing 6. SpringFox UI dependency
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.6.1</version>
</dependency>
Once you've added the springfox-swagger-ui
to the project, you can rebuild your app with mvn clean package
, then run it again with mvn spring-boot:run
. Now you'll see a new URL is available: http://localhost:8080/swagger-ui.html
. When you visit that URL, you should see something like the screenshot in Figure 1.
Figure 1. The Swagger UI
Looking at the UI in Figure 1, you can see the /greeting
endpoint is represented, along with its HTTP methods. Each method can be opened for more detail. Figure 2 shows detail for the /greeting GET
method.
Figure 2. Swagger UI endpoint detail
The model is represented, along with the parameters that are accepted. Clicking the Try it out! button sends a simple request to the endpoint.
Generating server-side code
So far you've seen how to create a basic OpenAPI definition from an existing Spring Web MVC app. You've also viewed the definition using a Swagger UI. Notice that you were able to get a huge amount of information out of the existing application without adding any special metadata to the application internals, except the one simple @EnableSwagger2
annotation.
Next we'll turn the flow around and see how we can use Swagger to generate code from an API spec.
Server stubbing
The Swagger ecosystem includes numerous plugins that support generating server stubs from OpenAPI specs. There are plugins for numerous languages and frameworks, and one of these is Spring Boot.
To start, take a look at this sample API hosted by the Swagger project in your browser: http://petstore.swagger.io/v2/swagger.json
.
This API definition is (yet another) manifestation of the classic PetStore example app; in this case, the OpenAPI specification for an online Pet Store application.
Installing the Swagger CLI
Swagger's code generation features are available via an executable JAR file that provides a command-line interface. To install the CLI, download the JAR file. Note that you'll be executing this JAR from a command prompt.
Generate a server stub from a Java API
Next you'll generate a server stub for the PetStore API. Remember to align the version number in the code below with that of the JAR you've just downloaded.
Listing 7. Using the Swagger CLI to create a Spring stub
java -jar [path-to-swagger-jar]/swagger-codegen-cli-2.2.2.jar generate \
-i http://petstore.swagger.io/v2/swagger.json \
-l spring \
-o output-directory/
Command-line options for generating the stub are as follows:
-i
indicates where the API definition is located; this can be either a file or a URL.-l
indicates what output format to use.-o
tells the CLI where to put the generated source code.
In this case, we're running the CLI with spring
as the -l
switch. As a result, you'll see application output with a similar structure to the Spring Boot Hello World application. This kind of functionality, where endpoints are automatically generated, is useful for stubbing out a new application when you already have a well-defined API spec.
Integrating server stubbing into a Maven build
If you want to add to an existing app in an ongoing way, you can integrate Swagger's codegen
functionality into your build via a Maven plugin. Returning to the pom.xml
for the Hello World app, add the plugin defined in Listing 8.
Listing 8. Using the Swagger CLI to create a Spring stub
<plugin>
<groupId>io.swagger</groupId>
<artifactId>swagger-codegen-maven-plugin</artifactId>
<version>2.2.2-SNAPSHOT</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>http://petstore.swagger.io/v2/swagger.json</inputSpec>
<language>spring</language>
<configOptions>
<sourceFolder>src/gen/java/main</sourceFolder>
</configOptions>
</configuration>
</execution>
</executions>
</plugin>