After defining API specs with RAML, we can use Spring MVC-RAML Maven plugin to generate API contract. With API specs using OpenAPI Specification, you can use OpenAPI Generator Maven plugin to do this. How is it in detail? We will find out together in this tutorial!
First, I will create a new Maven project:
for example.
As an example, I will use an API specs that I defined at this GitHub address https://github.com/khanhnguyenj/student-management-openapi-specs. You can get the content of this API specs, copy all the files and folders into the src/main/resources/api directory of our project:
Now we will declare the basic OpenAPI Generator Maven plugin as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
<plugin> <groupId>org.openapitools</groupId> <artifactId>openapi-generator-maven-plugin</artifactId> <version>7.15.0</version> <executions> <execution> <goals> <goal>generate</goal> </goals> <configuration> <inputSpec>${project.basedir}/src/main/resources/api/api.yml</inputSpec> <artifactId>openapi-generator-spring</artifactId> <groupId>com.huongdanjava.openapi</groupId> <generatorName>spring</generatorName> <apiPackage>com.huongdanjava.openapi.web</apiPackage> <modelPackage>com.huongdanjava.openapi.dto</modelPackage> <output>.</output> <configOptions> <delegatePattern>true</delegatePattern> <useSpringBoot3>true</useSpringBoot3> </configOptions> </configuration> </execution> </executions> </plugin> |
In the configuration section, the <inputSpec> tag is used to define the location of the API specs file that we want to generate source code for!
The <artifactId> tag is used to define the artifactId, and the <groupId> tag is used to define the groupId for the generated project.
OpenAPI Generator Maven plugin allows us to generate API contracts for many types of projects with different programming languages. You need to declare the Generator name to specify the type of project you want to generate. Generator list here. Here, I have declared to generate a Spring project.
Each Generator defines a lot of different config options, allowing us to generate the project as we want. You declare these config options in the <configOptions> tag. In the above example, I am using the spring generator and I am using two configOptions of this generator, <delegatePattern> and <useSpringBoot3>. The <delegatePattern> option with a value of true will use the Delegate Design Pattern in the implementation for the API specs, separating the interface class from the implementation. And the option <useSpringBoot3> is to generate Spring Boot 3 guys! There are many other config options, you can take a look here.
The <apiPackage> tag is used to declare the package name that the generated Controller classes will use, and <modelPackage> is related to the package that the generated DTO classes will use.
The <output> tag will be the folder containing the generated files. Here, I configure this tag with the value “.” to select the current directory. You can use the value “.” or ${project.basedir} is fine.
One point you should note is that when the OpenAPI Generator Maven plugin generates the Spring project for us, it will override the pom.xml file, so please back up the contents of the pom.xml file first. I will tell you how to configure the OpenAPI Generator Maven plugin to skip generating and override the pom.xml file later!
Now, if you run the project with “mvn clean compile”, and refresh the project, you will see the following results:
As you can see, the OpenAPI Generator Maven plugin has generated a Spring project.
Check the pom.xml file, as I said above, you will see that it overrides the pom.xml file as well. To skip generating and override this pom.xml file, please open the .openapi-generator-ignore file in the root directory of the project, and add the line “pom.xml”. Copy the contents of the pom.xml file that you have backed up, the next time you compile, this pom.xml file will not be overridden by the OpenAPI Generator Maven plugin anymore!
Run “mvn clean compile” again, and check the console log, you will see the following files are generated:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
[INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/dto/AddNewStudentResponse.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/dto/DeleteStudentResponse.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/dto/GetAllStudentsResponse.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/dto/GetStudentByIdResponse.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/dto/InlineObject.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/dto/InlineObject1.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/dto/Student.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/dto/UpdateStudentResponse.java [INFO] Implementation file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/web/StudentsApiController.java is not overwritten [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/web/StudentsApi.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/web/StudentsApiDelegate.java [INFO] Skipping generation of Webhooks. [INFO] Ignored /Users/khanhnguyenj/Documents/code/openapi-generator-spring/pom.xml (Ignored by rule in ignore file.) [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/README.md [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/org/openapitools/OpenApiGeneratorApplication.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/test/java/org/openapitools/OpenApiGeneratorApplicationTests.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/org/openapitools/RFC3339DateFormat.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/resources/application.properties [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/org/openapitools/configuration/HomeController.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/resources/openapi.yaml [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/org/openapitools/configuration/SpringDocConfiguration.java [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/src/main/java/com/huongdanjava/openapi/web/ApiUtil.java [INFO] Skipped /Users/khanhnguyenj/Documents/code/openapi-generator-spring/.openapi-generator-ignore (Skipped by supportingFiles options supplied by user.) [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/.openapi-generator/VERSION [INFO] writing file /Users/khanhnguyenj/Documents/code/openapi-generator-spring/.openapi-generator/FILES |
The package com.huongdanjava.openapi.dto will contain the DTOs, which are the Schema objects that we defined in the YAML file.
The package com.huongdanjava.openapi.web will contain Controller classes corresponding to URL requests, organized according to the Delegate Design Pattern. In addition, you will also see an ApiUtil class that defines the utility methods used in the Controller classes.
A src/main/resources/openapi.yaml file is also generated by the OpenAPI Generator Maven plugin. The contents of this openapi.yaml file are converted from the contents of our YAML file!
The application.properties file is also generated with the following properties:
1 2 3 |
server.port=8081 spring.jackson.date-format=org.openapitools.RFC3339DateFormat spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false |
You should also add this application.properties file to the .openapi-generator-ignore “src/main/resources/application.properties” file to avoid overriding!
The org.openapitools package defines a Controller that is used to run Swagger UI. This package also defines a class with the @SpringBootApplication annotation. We will use this class to run the Spring Boot application!
Now, if you run the application and go to http://localhost:8081/, you will see the following output:
This is the Swagger API documentation! Using it, you will know how many request URLs our application has exposed. The information of each request URL looks like, and we can use this Swagger API documentation to call the actual request URL.
Currently, we have not implemented URL requests. You can add a new class that implements the StudentsApiDelegate interface to do this. My example is as follows:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
package com.huongdanjava.openapi.web; import com.huongdanjava.openapi.dto.GetStudentByIdResponse; import com.huongdanjava.openapi.dto.Student; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; @Service public class StudentApiDelegateImpl implements StudentsApiDelegate { @Override public ResponseEntity<GetStudentByIdResponse> getStudentById(String id) { GetStudentByIdResponse getStudentByIdResponse = new GetStudentByIdResponse(); getStudentByIdResponse.success(true); Student student = new Student(); student.setId(1L); student.setCode("123"); student.setName("HDJ"); getStudentByIdResponse.setData(student); return ResponseEntity.ok(getStudentByIdResponse); } } |
The result, if we request to the URL http://localhost:8081/api/students/1 now, is as follows:
If you generate a Spring Webflux project, please add a config option “reactive”:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<plugin> <groupId>org.openapitools</groupId> <artifactId>openapi-generator-maven-plugin</artifactId> <version>7.15.0</version> <executions> <execution> <goals> <goal>generate</goal> </goals> <configuration> ... <configOptions> <delegatePattern>true</delegatePattern> <useSpringBoot3>true</useSpringBoot3> <reactive>true</reactive> </configOptions> </configuration> </execution> </executions> </plugin> |
Of course, then, you must add a Spring Webflux dependency so that the project can be compiled:
1 2 3 4 |
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> |