Skip to content
This repository has been archived by the owner on Apr 16, 2023. It is now read-only.

georgwittberger/openapi-contract-example

Repository files navigation

OpenAPI Contract Example

This example project demonstrates how to connect a browser-based frontend application, a backend-for-frontend application and a microservice application via HTTP interfaces using contract-first OpenAPI specifications and automatic code generation.

Getting started

Prerequisites

  • Java Development Kit 10 (or higher)

  • Node.js 8.9.x and NPM 5.6.x (or higher)

  • Free local ports: 10080, 10081, 10082

Running the example

  1. Clone the Git repository.

  2. Open a terminal in the subdirectory microservice-application and run mvnw spring-boot:run. This will start the microservice on local port 10082.

  3. Open a terminal in the subdirectory bff-application and run mvnw spring-boot:run. This will start the BFF on local port 10081.

  4. Open a terminal in the subdirectory frontend-application, then first run npm install and then npm start. This will start a HTTP server for the frontend on local port 10080.

  5. Open your browser and navigate to http://localhost:10080/

The microservice application

The Spring Boot application in the subdirectory microservice-application represents a backend service which provides a rather general JSON-API for a specific business domain - in this example an API to retrieve messages.

Characteristics of the microservice

  • The provided JSON-API is defined using the OpenAPI specification file src/main/resources/static/api/v1/microservice-api.json.

  • During the Maven build process the openapi-generator-maven-plugin is used to generate a Spring-annotated Java interface and Java model classes for the response data structure directly from the specification file.

  • The generated Java interface is implemented by the MessagesController class. This controller implementation is additionally annotated with @RequestMapping("/api/v1") to serve the API underneath that base path.

  • There is an automated test for the controller implementation in the MessagesControllerTests class to verify the runtime API compliance.

Design decisions for the microservice

  • The OpenAPI specification file is intentionally located inside the static directory to have Spring Boot serve it automatically at http://localhost:10082/api/v1/microservice-api.json

  • Only a Java interface with Spring @RequestMapping annotations is generated from the specification file to separate the interface contract from the server implementation. The goal is to generate the Java interface during each Maven build directly from the specification file but still have a persistent implementation of that interface which is written by developers manually. As soon as operations or parameters are modified in the specification file it will force the server implementation to adapt to these changes.

  • Java model classes are generated from the specification file to ensure compile-time check of these classes used to bind API requests and responses. When some request or response data structure is changed in the specification file it will force the server implementation to adapt to these changes.

  • The Spring controller implementation is mapped to the base path /api/v1 rather than setting the context path of the whole application to this path. This allows the application to still serve resources outside of this versioned API path and allows to implement even two versions of the API within the same application version.

The backend-for-frontend application

The Spring Boot application in the subdirectory bff-application represents a frontend-oriented API gateway - also called a backend-for-frontend (BFF) - which provides a simple API in JSON format to allow the frontend application to retrieve a view on the data which is really required by the frontend - in this example only the plain text of a specific message (without any technical data like ID). The message data is loaded via request to the microservice application and transformed to the simpler view model.

Characteristics of the backend-for-frontend

  • The provided API is defined using the OpenAPI specification file src/main/resources/static/api/v1/bff-api.json.

  • During the Maven build process the openapi-generator-maven-plugin is used to generate a Spring-annotated Java interface and Java model classes for the response data structure directly from the BFF specification file.

  • The generated Java interface for the BFF API is implemented by the MessagesController class. This controller implementation is additionally annotated with @RequestMapping("/api/v1") to serve the API underneath that base path.

  • There is an automated test for the controller implementation in the MessagesControllerTests class to verify the runtime API compliance.

  • The consumed JSON-API of the microservice is defined by the OpenAPI specification file src/main/openapi/microservice-api.json. It is an exact copy of the file from the microservice application.

  • During the Maven build process the openapi-generator-maven-plugin is used to generate a Java interface from that microservice specification file.

  • The generation of the Java interface for the microservice API is customized by the Mustache template located at src/main/openapi/templates/api.mustache. It is a slightly modified version of the original template.

  • The generated Java interface for the microservice API is extended by the MessagesClient interface to implement an OpenFeign client named "messages" which is used by the BFF to access the API of the microservice.

  • There is an automated test for the OpenFeign client in the MessagesClientTests class to verify the runtime API compliance. The test is supported by an embedded WireMock server which is configured by the files located in src/test/resources/mocks/messages.

Design decisions for the backend-for-frontend

  • The OpenAPI specification file is intentionally located inside the static directory to have Spring Boot serve it automatically at http://localhost:10081/api/v1/bff-api.json

  • For providing the BFF API, only a Java interface with Spring @RequestMapping annotations is generated from the BFF specification file to separate the interface contract from the server implementation. The goal is to generate the Java interface during each Maven build directly from the specification file but still have a persistent implementation of that interface which is written by developers manually. As soon as operations or parameters are changed in the BFF specification file it will force the server implementation to adapt to these changes.

  • For consuming the microservice JSON-API, a Java interface with Spring @RequestMapping annotations is generated from the microservice specification file to support implementation of an OpenFeign client which is always compliant with the specification. When some operation or parameter of the microservice API is modified the BFF will be forced to adapt to these changes.

  • The Java interface generation for the OpenFeign client is customized for two reasons:

    1. When using Java 8 language level with the OpenAPI generator there is no way to turn off the generation of default methods in the Java interface. But this is problematic with Feign clients because such methods do not really execute requests in the end. Therefore, the generation of the default method stub must be removed.

    2. Methods generated for API operations only have a @RequestMapping for their pure operation path, not including any base path of the API. This is problematic with Feign clients in combination with Ribbon and service discovery because it is impossible to configure the base path /api/v1 for a Feign client but still preserving the flexibility of Ribbon load-balancing. Therefore, the request mapping in the Java interface must include the base path of the API given by the variable {{{contextPath}}}.

  • Java model classes are generated from the BFF and microservice specification file to ensure compile-time check of these classes used to bind API requests and responses. When some request or response data structure is changed in the specification files it will force the server implementation to adapt to these changes.

  • The Spring controller implementation is mapped to the base path /api/v1 rather than setting the context path of the whole application to this path. This allows the application to still serve resources outside of this versioned API path and allows to implement even two versions of the API within the same application version.

The frontend application

The frontend application in the subdirectory frontend-application represents a browser-based JavaScript application which makes use of the API exposed by the BFF application to display that server-provided data on the web page. The application code is written in TypeScript and packaged into a JavaScript bundle using Webpack.

Characteristics of the frontend

  • The consumed API of the BFF is defined by the OpenAPI specification file src/api/bff-api.json. It is an exact copy of the file from the BFF application.

  • The openapi-generator-maven-plugin is used during the build process to generate a fetch-based HTTP client module from the specification file. This little Maven build is embedded as part of the NPM build using a Maven wrapper.

  • The main module src/index.ts imports the generated HTTP client module located at src/api/bff-api-client and uses it to retrieve the message text from the BFF.

Design decisions for the frontend

  • Automatically generating the HTTP client in TypeScript ensures that the frontend always uses a client implementation which is compliant with the OpenAPI specification file. As soon as the operations, parameters or message data structures are modified the frontend is forced to adapt to these changes.

  • TypeScript is a crucial tool decision to make sure that correct interaction of the frontend business logic with the generated HTTP client is checked already at compile time.

  • The embedded Maven build is required to stick with the official OpenAPI tooling for code generation. All pure Node.js based generators were not really ready for OpenAPI 3.0 at the time of this writing.

The Swagger UI server

The subdirectory spec-server contains a small ExpressJS application which can be used to serve the Swagger UI for multiple OpenAPI specification files. This can be helpful for development teams to have an overview of all the available APIs and gives them a nice view of the specifications.

Starting the server

  1. Open a terminal in the subdirectory spec-server, then first run npm install and then npm start. This will start the ExpressJS server on local port 3000.

  2. Open your browser and navigate to http://localhost:3000/

Adding specifications

The OpenAPI specifications selectable in the Swagger UI are located in the subdirectory specs of the server application. More specs can be added by simply placing files in this directory.

Note that the server must be restarted for new files to appear in the list.

Conclusion

Modern web APIs can be easily defined using the OpenAPI specification. These specification files can be designed before any real implementation of applications begins (contract first). As soon as the API has been defined the specification can be shared with several development teams to stark working on client and server implementations in parallel.

The OpenAPI specification files can also be used to generate source code which helps to implement API compliant servers and clients. There is the possibility not only to generate an initial project from the specification but also to have API-related source code generated again during each build process. This helps to keep the application code in sync with the API specification.

Additionally, having the OpenAPI specification files for both the provided and consumed APIs inside each application makes it easier to find out which versions of connected applications can play together.

License

MIT

About

Example project to demonstrate how to connect frontend, BFF and microservice via contract-first OpenAPI specification

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published