Java API Server

Generate API Server (Java)

Summary

This tutorial will explain how to generate an API server in java using an API definition.

Info

Prerequisite Environment Setup

Tip

  • ${PWD} works on Linux, MacOS, and Windows (via Powershell)
  • %cd% works on Windows (via cmd)
  • $(cygpath -m -a "$(pwd)") works on Windows (via Cygwin)

Validate API Definition

docker run --rm -v ${PWD}:/local -w /local swaggerapi/swagger-codegen-cli validate \
    -i task-tracker-api.yaml

Generate Server

Filename: codegen_config.json

{
  "artifactId": "tracker",
  "groupId": "com.example.tracker",
  "basePackage": "com.example.tracker",
  "apiPackage": "com.example.tracker.api",
  "configPackage": "com.example.tracker.config",
  "modelPackage": "com.example.tracker.model",
  "hideGenerationTimestamp": true,
  "dateLibrary": "java8",
  "useBeanValidation": true,
  "licenseUrl": ""
}
docker run --rm -v ${PWD}:/local -w /local swaggerapi/swagger-codegen-cli generate \
    -i task-tracker-api.yaml \
    --lang spring \
    -c codegen_config.json

Output:

[main] INFO io.swagger.parser.Swagger20Parser - reading from task-tracker-api.yaml
[main] INFO io.swagger.codegen.ignore.CodegenIgnoreProcessor - No .swagger-codegen-ignore file found.
[main] INFO io.swagger.codegen.DefaultCodegen - Invoker Package Name, originally not set, is now dervied from api package name: com.example.tracker
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/model/Task.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/model/User.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/api/TasksApiController.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/api/TasksApi.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./pom.xml
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./README.md
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/config/HomeController.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/Swagger2SpringBoot.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/RFC3339DateFormat.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/resources/application.properties
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/api/ApiException.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/api/ApiResponseMessage.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/api/NotFoundException.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/api/ApiOriginFilter.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./src/main/java/com/example/tracker/config/SwaggerDocumentationConfig.java
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./.swagger-codegen-ignore
[main] INFO io.swagger.codegen.AbstractGenerator - writing file /local/./.swagger-codegen/VERSION

Files:

./.swagger-codegen
./.swagger-codegen/VERSION
./.swagger-codegen-ignore
./codegen_config.json
./pom.xml
./README.md
./src
./src/main
./src/main/java
./src/main/java/com
./src/main/java/com/example
./src/main/java/com/example/tracker
./src/main/java/com/example/tracker/api
./src/main/java/com/example/tracker/api/ApiException.java
./src/main/java/com/example/tracker/api/ApiOriginFilter.java
./src/main/java/com/example/tracker/api/ApiResponseMessage.java
./src/main/java/com/example/tracker/api/NotFoundException.java
./src/main/java/com/example/tracker/api/TasksApi.java
./src/main/java/com/example/tracker/api/TasksApiController.java
./src/main/java/com/example/tracker/config
./src/main/java/com/example/tracker/config/HomeController.java
./src/main/java/com/example/tracker/config/SwaggerDocumentationConfig.java
./src/main/java/com/example/tracker/model
./src/main/java/com/example/tracker/model/Task.java
./src/main/java/com/example/tracker/model/User.java
./src/main/java/com/example/tracker/RFC3339DateFormat.java
./src/main/java/com/example/tracker/Swagger2SpringBoot.java
./src/main/resources
./src/main/resources/application.properties
./task-tracker-api.yaml

Build Server

Create a simple Dockerfile to use for running the server. There are multiple different approaches for compiling the server within a docker image. This is the simplest but there are more efficient approaches.

Filename: Dockerfile

FROM maven:3.5-jdk-8

WORKDIR /opt/tracker

COPY pom.xml /opt/tracker/pom.xml
COPY src /opt/tracker/src

RUN mvn -e package -Dmaven.test.skip=true -DskipTests

CMD ["java", "-jar", "/opt/tracker/target/tracker-1.0.0.jar"]
EXPOSE 8080
docker build --tag tracker-javalocal .

Run Server

docker run -d --rm -p 8080:8080 tracker-javalocal

Example Requests and Responses

Request

curl -D - -s localhost:8080/v1/tasks

Response

HTTP/1.1 415
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 10 Oct 2017 19:24:11 GMT

{"timestamp":"2017-10-10T19:24:11.807Z","status":415,"error":"Unsupported Media Type","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'null' not supported","path":"/v1/tasks"}

Request

curl -D - -s -H "Content-type: application/json" localhost:8080/v1/tasks

Response

HTTP/1.1 200
Content-Length: 0
Date: Tue, 10 Oct 2017 19:26:14 GMT

Request

curl -D - -s localhost:8080/v1/tasks/1

Response

HTTP/1.1 415
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 10 Oct 2017 19:24:40 GMT

{"timestamp":"2017-10-10T19:24:40.968Z","status":415,"error":"Unsupported Media Type","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'null' not supported","path":"/v1/tasks/1"}

Request

curl -D - -s -H "Content-type: application/json" localhost:8080/v1/tasks/1

Response

HTTP/1.1 400
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 10 Oct 2017 19:26:56 GMT
Connection: close

{"timestamp":"2017-10-10T19:26:56.527Z","status":400,"error":"Bad Request","exception":"org.springframework.web.method.annotation.MethodArgumentTypeMismatchException","message":"Failed to convert value of type 'java.lang.String' to required type 'java.util.UUID'; nested exception is java.lang.IllegalArgumentException: Invalid UUID string: 1","path":"/v1/tasks/1"}

Request

curl -D - -s -H "Content-type: application/json" localhost:8080/v1/tasks/6ba2cd8d-d548-41b6-8b5c-98cc45af2313

Response

HTTP/1.1 200
Content-Length: 0
Date: Tue, 10 Oct 2017 19:30:04 GMT

Request

curl -D - -s -X PUT localhost:8080/v1/tasks/1

Response

HTTP/1.1 415
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 10 Oct 2017 19:25:08 GMT

{"timestamp":"2017-10-10T19:25:08.538Z","status":415,"error":"Unsupported Media Type","exception":"org.springframework.web.HttpMediaTypeNotSupportedException","message":"Content type 'null' not supported","path":"/v1/tasks/1"}

Request

curl -D - -s -X PUT -H "Content-type: application/json" localhost:8080/v1/tasks/1

Response

HTTP/1.1 400
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 10 Oct 2017 19:29:14 GMT
Connection: close

{"timestamp":"2017-10-10T19:29:14.464Z","status":400,"error":"Bad Request","exception":"org.springframework.web.method.annotation.MethodArgumentTypeMismatchException","message":"Failed to convert value of type 'java.lang.String' to required type 'java.util.UUID'; nested exception is java.lang.IllegalArgumentException: Invalid UUID string: 1","path":"/v1/tasks/1"}

Request

curl -D - -s -X PUT -H "Content-type: application/json" localhost:8080/v1/tasks/6ba2cd8d-d548-41b6-8b5c-98cc45af2313

Response

HTTP/1.1 400
Content-Type: application/json;charset=UTF-8
Transfer-Encoding: chunked
Date: Tue, 10 Oct 2017 19:30:59 GMT
Connection: close

{"timestamp":"2017-10-10T19:30:59.651Z","status":400,"error":"Bad Request","exception":"org.springframework.http.converter.HttpMessageNotReadableException","message":"Required request body is missing: public default org.springframework.http.ResponseEntity<com.example.tracker.model.Task> com.example.tracker.api.TasksApi.updateTask(java.util.UUID,com.example.tracker.model.Task,java.util.UUID,java.lang.String) throws java.lang.Exception","path":"/v1/tasks/6ba2cd8d-d548-41b6-8b5c-98cc45af2313"}

Request

curl -D - -s -X PUT -H "Content-type: application/json" -d '{}' localhost:8080/v1/tasks/6ba2cd8d-d548-41b6-8b5c-98cc45af2313

Response

HTTP/1.1 200
Content-Length: 0
Date: Tue, 10 Oct 2017 19:32:38 GMT