Arjun Patel
Personal website and dumping ground for my technical notes.

Generating Spring Boot web server from OpenAPI spec

#java #maven #openapi #spring-boot

openapi-generator-maven-plugin

Generate the API spec during the generate-sources phase using this build plugin:

<plugin>
  <groupId>org.openapitools</groupId>
  <artifactId>openapi-generator-maven-plugin</artifactId>
  <version>${openapi-generator-maven-plugin.version}</version>
  <executions>
    <execution>
      <id>generate-server-code</id>
      <goals>
        <goal>generate</goal>
      </goals>
      <phase>generate-sources</phase>
      <configuration>
        <inputSpec>${project.build.directory}/generated-sources/api.yml</inputSpec>
        <generatorName>spring</generatorName>
        <apiPackage>${project.groupId}.YOUR_PROJECT.infrastructure.web.controller</apiPackage>
        <modelPackage>${project.groupId}.YOUR_PROJECT.infrastructure.web.view</modelPackage>
        <invokerPackage>${project.groupId}.YOUR_PROJECT</invokerPackage>
        <templateDirectory>${project.basedir}/src/main/resources/openapi</templateDirectory>
        <strictSpec>true</strictSpec>
        <enablePostProcessFile>false</enablePostProcessFile>
        <configOptions>
          <delegatePattern>true</delegatePattern>
          <configPackage>${project.groupId}.YOUR_PROJECT.infrastructure.web.config</configPackage>
          <useBeanValidation>true</useBeanValidation>
          <useOptional>true</useOptional>
          <useSpringBoot3>true</useSpringBoot3>
        </configOptions>
      </configuration>
    </execution>
  </executions>
</plugin>

Change the inputSpec so that it points to your actual input spec.

I'm using version 7.12.0 of the generator plugin in this example.

Getting Maven to recognise generated sources

Put this plugin definition directly after the openapi-maven-generator-plugin plugin definition:

<plugin>
  <groupId>org.codehaus.mojo</groupId>
  <artifactId>build-helper-maven-plugin</artifactId>
  <executions>
    <execution>
      <id>add-generate-server-sources</id>
      <goals>
        <goal>add-source</goal>
      </goals>
      <phase>generate-sources</phase>
      <configuration>
        <sources>
          <source>${project.build.directory}/generated-sources/openapi/src/main/java</source>
        </sources>
      </configuration>
    </execution>
    <execution>
      <id>add-generated-server-test-sources</id>
      <goals>
        <goal>add-test-source</goal>
      </goals>
      <phase>generate-test-sources</phase>
      <configuration>
        <sources>
          <source>${project.build.directory}/generated-sources/openapi/src/test/java</source>
        </sources>
      </configuration>
    </execution>
  </executions>
</plugin>

It defines two executions, the first is used to tell Maven where the generated source code is, and the second one is to tell Maven where the generated test code is.

Dependencies

You need some or all of these dependencies, I couldn't be bothered to check exactly which ones...:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
  <groupId>org.springdoc</groupId>
  <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
  <version>${springdoc-openapi-starter-webmvc-ui.version}</version>
</dependency>
<dependency>
  <groupId>org.openapitools</groupId>
  <artifactId>jackson-databind-nullable</artifactId>
  <version>${jackson-databind-nullable.version}</version>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>