DEV Community

Anatolii Kozlov
Anatolii Kozlov

Posted on • Edited on

3

OpenAPI Generation with Spring

1 Overview

In this article we consider example of using code generation by OpenAPI specification. This is the API-First approach to building service. Code generator will be used for generating code in java-spring application. Full source code of example project on GitHub

2 Specification

Let's start with writing simple OpenAPI specifications. Here we use an example with 'Get' and 'Create' operations for some Clients.

openapi: 3.0.3
info:
  title: client-api
  description: 'Client API'
  version: 1.0.0
paths:
  /client:
    get:
      parameters:
        - name: id
          in: query
          required: true
          schema:
            type: string
            format: uuid
            description: Client id
      responses:
        200:
          description: Success response with client
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientResponse"
    post:
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/Client'
        required: true
      responses:
        200:
          description: Success response with client
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ClientResponse"
components:
  schemas:
    ClientResponse:
      type: object
      properties:
        success:
          type: boolean
          description: Operation success flag
        message:
          type: string
          description: Error description
        client:
          $ref: "#/components/schemas/Client"
      required:
        - success
        - message
        - object
    Client:
      type: object
      description: Client data
      properties:
        id:
          type: string
          format: uuid
          description: Client unique id
        name:
          type: string
          description: Client name
          maximum: 32
          minimum: 2
        dateOfBirth:
          type: string
          format: date
        createdAt:
          type: string
          description: Date of client registration
          format: date-time
        banned:
          type: boolean
          description: Ban flag
        countAccounts:
          type: integer
          description: Count of client accounts
          minimum: 0
        serviceRate:
          type: string
          description: Service rate
          enum:
            - "FREE"
            - "STANDARD"
            - "VIP"
Enter fullscreen mode Exit fullscreen mode

3 Setup dependencies

Let's setup OpenAPI generator maven plugin

<plugin>
    <groupId>org.openapitools</groupId>
    <artifactId>openapi-generator-maven-plugin</artifactId>
    <version>6.2.0</version>
    <executions>
        <execution>
            <goals>
                <goal>generate</goal>
            </goals>
            <configuration>
                <inputSpec>${project.basedir}/specs/client-api.yaml</inputSpec>
                <generatorName>spring</generatorName>
                <apiPackage>dev.toliyansky.openapi.api</apiPackage>
                <modelPackage>dev.toliyansky.openapi.model</modelPackage>
            </configuration>
        </execution>
    </executions>
</plugin>
Enter fullscreen mode Exit fullscreen mode

4 Code examples

After run mvn clean install generated classes will be placed into target directory.

Image description

To implement the HTTP route we should create a class that extends generated controller.

@RestController
@RequestMapping("/api")
public class ClientController extends ClientApiController {

    private final RandomExceptionService randomExceptionService;

    public ClientController(NativeWebRequest request, RandomExceptionService randomExceptionService) {
        super(request);
        this.randomExceptionService = randomExceptionService;
    }

    @Override
    public ResponseEntity<ClientResponse> clientGet(UUID id) {
        var clientResponse = new ClientResponse();
        try {
            var client = new Client(); // Stub. But in real project get client from service level.
            clientResponse.setSuccess(true);
            clientResponse.setClient(client);
            randomExceptionService.generateException50percentChance();
            return ResponseEntity.ok(clientResponse);
        } catch (Exception e) {
            e.printStackTrace();
            clientResponse.setSuccess(false);
            clientResponse.setMessage(e.getMessage());
            clientResponse.setClient(null);
            return ResponseEntity.internalServerError().body(clientResponse);
        }
    }

    @Override
    public ResponseEntity<ClientResponse> clientPost(Client client) {
        var clientResponse = new ClientResponse();
        try {
            // Do some actions with client in service level.
            client.id(UUID.randomUUID());
            clientResponse.setSuccess(true);
            clientResponse.setClient(client);
            randomExceptionService.generateException50percentChance();
            return ResponseEntity.ok(clientResponse);
        } catch (Exception e) {
            e.printStackTrace();
            clientResponse.setSuccess(false);
            clientResponse.setMessage(e.getMessage());
            clientResponse.setClient(null);
            return ResponseEntity.internalServerError().body(clientResponse);
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Now we can test it with HTTP request

### Create client
POST http://localhost:8080/api/client
Content-Type: application/json
------
HTTP/1.1 200 
Content-Type: application/json
{
  "success": true,
  "message": null,
  "client": {
    "id": "6796a90a-314a-4667-a7cc-dfbe592b27e5",
    "name": "Anatoliy",
    "dateOfBirth": "2020-01-01",
    "createdAt": "2020-01-01T00:00:00Z",
    "banned": false,
    "countAccounts": 3,
    "serviceRate": "VIP"
  }
}

### Get client by id
GET http://localhost:8080/api/client?id=6796a90a-314a-4667-a7cc-dfbe592b27e5
Accept: application/json
------
HTTP/1.1 200 
Content-Type: application/json
{
  "success": true,
  "message": null,
  "client": {
    "id": "6796a90a-314a-4667-a7cc-dfbe592b27e5",
    "name": "Anatoliy",
    "dateOfBirth": "2020-01-01",
    "createdAt": "2020-01-01T00:00:00Z",
    "banned": false,
    "countAccounts": 3,
    "serviceRate": "VIP"
  }
}
Enter fullscreen mode Exit fullscreen mode

5 Conclusion

In this article, we saw how to generate a java-spring service with an API-First approach with OpenAPI YAML specification and OpenAPI generator maven plugin.

Full source code of example project on GitHub

Top comments (0)

Great read:

Is it Time to go Back to the Monolith?

History repeats itself. Everything old is new again and I’ve been around long enough to see ideas discarded, rediscovered and return triumphantly to overtake the fad. In recent years SQL has made a tremendous comeback from the dead. We love relational databases all over again. I think the Monolith will have its space odyssey moment again. Microservices and serverless are trends pushed by the cloud vendors, designed to sell us more cloud computing resources.

Microservices make very little sense financially for most use cases. Yes, they can ramp down. But when they scale up, they pay the costs in dividends. The increased observability costs alone line the pockets of the “big cloud” vendors.