Programming with Java APIs, Part 2: API definitions

Use API definitions to generate a Spring Web MVC app with an Angular 2 frontend

moonjazz (CC BY-SA 2.0)

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 infrastructure
  • Greeting, a model class
  • GreetingController, 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.

Matthew Tyson

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.

Matthew Tyson

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>

Running the generate goal with mvn generate or as part of mvn package automatically creates the source code in the src/gen/java/main path, which you have defined in the sourceFolder element.

In a real project, of course, the inputSpec parameter will either be a URL hosted by your organization (where changes to the API are posted) or a file checked out with version control. In either case, API changes will be reflected by the generated code, ready for project integration.

Generating client-side code

With the backend stubbed out, you're ready to generate some client-side code. As I explained in Part 1, API definitions are the common point of contact between the frontend and backend in web applications. Because of this, we can use the the same API definition to output code for both the server and the client side.

Swagger's client-side language support

Similar to the backend, Swagger supports a variety of frontend technologies. In this case, let's say we want to generate a client for our Hello World application. Swagger offers several generators capable of outputting clients. You can view them on Swagger's GitHub page, or you could execute the following in your CLI: java -jar swagger-codegen-cli-2.2.2.jar langs.

An easy way to get a headstart on building a UI or other API consumer is to combine the /v2/api-docs endpoint we created at the beginning of this article with a client generator. Swagger supports technologies like Java, Node.js, and Angular.js. Right now, I want to show you an interesting option that will output code for several of these.

Assuming the server that we created earlier is running, this endpoint should be available on your local system: http://localhost:8080/v2/api-docs. Now, take the Swagger swagger-codegen JAR that you previously downloaded and point it at your local API spec. I've done this in Listing 9.

Listing 9. Generate client examples with the Swagger CLI


java -jar swagger-codegen-cli-2.2.2.jar generate \
  -i http://localhost:8080/v2/api-docs \
  -l html2 \
  -o client/html2

In Listing 9, we are again executing the swagger-codegen JAR with the generate command. This time, however, we are directing it via the -i switch to the local http://localhost:8080/v2/api-docs spec. Note that I've also set the plugin language to html2 with the -l flag. The -o variable specifies the output directory.

When you run this command, you will get an index.html file in your output directory. This is a static file that represents a client for the API at the time you run it. If you open this HTML file in your browser, you'll see something like Figure 3.

Matthew Tyson

Figure 3. Client API examples in index.html

The interesting part of this particular plugin is that it actually outputs examples for each endpoint in several languages. Within each endpoint, you'll see a tab for Java, JavaScript, Python, and so on. You can use any of these as a starting point to code the application frontend.

An API-driven Angular 2 client

Next we'll develop a more involved client example based on Angular 2, using the same tools we've already explored.

Begin by cloning the Angular 2 WebPack boilerplate code. You can do this by typing git clone https://github.com/kozlice/angular2-webpack-boilerplate.git on your command line. Next, prepare the app by typing npm install in your newly created /angular2-webpack-boilerplate directory. Finally, test the app by entering the npm start command.

You now have a simple Angular 2 client running at http://localhost:9045/. If you visit that URL in your browser, you should see something like Figure 4.

Matthew Tyson

Figure 4. Angular starter UI 

Using the Swagger Codegen snapshot

The PetStore app we used to generate our API includes a Spring backend, which we'll now use to build out some UI elements. For this you will need to install the codegen plugin for Angular 2.

The first step is to install the codegen repository to your local system, placing it anywhere that is convenient to remember. To install codegen, just enter the following command on your CLI: git clone https://github.com/swagger-api/swagger-codegen.git

Inside the newly created swagger-codegen directory are several Maven projects. We're interested in the CLI project, so change the directory to /swagger-codegen/modules/swagger-codegen-cli and build the project from source by typing mvn package. When Maven finishes its work, the target directory will contain the new swagger-codegen-cli JAR file, which we'll use to generate the Angular 2 code.

Return now to the root of the Angular 2 application, first shown in Listing 9. You'll be generating code into the src/app directory, as shown in Listing 10. Listing 10 is similar to Listing 9, where you created the HTML client, but in this case you are using the typescript-angular2 plugin and the snapshot JAR.

Listing 10. Using Codegen snapshot to generate Angular 2 code


java -jar >path-to-swagger-cli-project>/swagger-codegen/modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \
  -i http://localhost:8080/v2/api-docs \
  -l typescript-angular2 \
  -o ./src/app

Angular 2's Codegen output

If you look in the src/app directory, you'll see two new folders: /api and /model. These contain the service implementation and model classes for the API.

If you look in the /model/Greeting.ts file, you'll see something like Listing 11.

Listing 11. Generated Angular 2 model for the Greeting schema


export interface Greeting {
    content?: string;

    id?: number;

}

Listing 11 shows a class modelling Greeting, which we originally created as a Spring Web MVC model class. So you've taken a backend model, generated an API schema for it, and generated a frontend model from that. Not bad! Note, too, that the class in Listing 11 has content and id fields, just as you would expect from a Spring app.

Similarly, in the /api directory, you'll find an expression of the path-mapping portion of the Greeting API in the GreetingcontrollerApi.ts file. For instance, Listing 12 has the getGreetingsUsingGET() method, which provides functionality for executing a GET against the Greeting endpoint.

Listing 12. getGreetingsUsingGET()


public getGreetingsUsingGET(extraHttpRequestParams?: any): Observable>Array>models.Greeting>> {
        return this.getGreetingsUsingGETWithHttpInfo(extraHttpRequestParams)
            .map((response: Response) => {
                if (response.status === 204) {
                    return undefined;
                } else {
                    return response.json() || {};
                }
            });
    }

Angular 2 uses observables, so getGreetingsUsingGET is in this idiom. We'll see how to use this in the Angular 2 in just a moment. First, we need to make two small changes that will get everything working together.

Connecting Angular 2 and Spring

At the head of the file, you'll find a variable assignment for basePath. For the sake of this example, change the variable to point to an HTTP localhost, as shown in Listing 13. (Of course, in a real-world app you would more likely modify the backend to support HTTPS.)

Listing 13. Updating basePath


protected basePath = 'http://localhost:8080';

Next, you'll modify the Spring app's GreetingController class. Modify the Greeting service method to allow cross-origin requests, as seen in Listing 14. This will allow the Spring app to handle requests from the Angular 2 app running on a different port.

Listing 14. Allowing cross-origin requests in Spring Web MVC


@CrossOrigin(origins = "http://localhost:9045")
public Greeting greeting(@RequestParam(value="name", defaultValue="World") String name) {
    return new Greeting(counter.incrementAndGet(),
                        String.format(template, name));
}

Using the generated Angular 2 code

Next you'll make two more changes to the Angular application. First, update the article/article-list.html file as seen in Listing 15.

Listing 15. article-list.html Modifications


>div *ngIf="isLoading">
    Loading&hellip;
>/div>

{{ greeting }}

>article *ngFor="let article of articles" class="article-list-item">
    >h4>>a [routerLink]="['/articles', article.id]">{{ article.title }}>/a>>/h4>
>/article>

Here you are adding an Angular template variable with {{ greeting }}, which is where the UI will output the greeting from the Spring backend.

Note that article-list.html file is an Angular template. It provides markup for the article-list component, which is defined in the /article/article-list.component.ts file. In Listing 16, I've updated that file with changes that are vital to making use of the generated Angular code. Apply these changed to article-list.component.ts, we'll take a look at this file in detail next.

Listing 16. Updates to article-list.component.ts


import { Component, ViewEncapsulation, OnInit, OnDestroy } from '@angular/core';

import { ArticleService } from './article.service';
import { Article } from './article.model';
import { Response } from '@angular/http';
import { GreetingcontrollerApi } from '../api/api'; // 1

/**
 * A simple component, which fetches articles list from HTTP API and displays them.
 */
@Component({
    selector: 'da-article-list',
    templateUrl: './article-list.html',
    styleUrls: ['./article-list.scss'],
    encapsulation: ViewEncapsulation.Emulated,
    providers: [ArticleService, GreetingcontrollerApi] // 2
})
export class ArticleListComponent implements OnInit, OnDestroy {
    private articles: Article[];
    private error: Response;
    private isLoading: boolean = true;
    greeting: string = ""; // 3

    constructor(private articleService: ArticleService, private greetingService: GreetingcontrollerApi) {} // 4

    ngOnInit(): void {
        this.articleService.getAll().subscribe(
            (data)  => this.articles = data,
            (error) => this.error = error,
            ()      => this.isLoading = false
        );
        param = { "name": "test" };
        this.greetingService.greetingUsingGET(param).subscribe( // 5
          (data) => this.greeting = data.content,
          (error) => this.error = error,
          () => {}
        );
    }

    ngOnDestroy(): void {}
}

Notes about the code

Listing 16 is commented with numbers, which I'll use to explain how ArticleListComponent works.

  1. Import the GreetingcontrollerApi class from the /api/api module.
  2. Add the GreetingcontrollerApi as a "provider." This allows the service to be injected into the class, in a fashion similar to Spring auto-wiring.
  3. Add the greeting member variable to the component. greeting is the variable the {{ greeting }} template variable will resolve to.
  4. Add the private greetingService: GreetingcontrollerApi argument to the constructor. This is where the controller is actually injected into the class. It will be available as a private member variable.
  5. Add the this.greetingService.greetingUsingGET(param) call. This is where you make use of the generated service class. The call returns an observable, which you will subscribe to in order to obtain the result.

Testing the app

Now when you run the Angular and Spring applications together, combining your backend and frontend code, you should see the UI in Figure 5.

Matthew Tyson

Figure 5. The Angular UI with the Spring API greeting

Conclusion

Programming with APIs is a valuable enhancement to the software development lifecycle, in part because using APIs helps formalize our thinking about systems. As you've seen in this two-part article, capturing that thought in Java API definitions can be done at every stage of development.

In Part 1 we used OpenAPI to help define a BikeParts app, and you learned how using APIs formalizes requirements gathering. In Part 2, we've used automated to tools to generate application code from Java API definitions. With the PetStore app you saw how to generate frontend and backend code from an API. You also saw how to use application code to create an API.

APIs have been part of software development for decades, but they've moved to center stage in software development only recently. As the tooling around Java APIs continues to mature and improve, we will have even more options for putting APIs at the heart of Java-based software development. It will be interesting to see how API-driven development evolves over the next few years.