Skip to content

Spring Boot CRUD API

Let’s expose user CRUD API without implementing any HTML views.

You can use spring initializr to create the project skeleton.

spring-initializr-user-crud

The project structure is:

  • Directorysrc.main
    • Directoryjava.com.pietropouzzi.user_crud_server
      • Directorycontrollers
        • UserController.java
      • Directorymodels
        • User.java
        • UserRequest.java
      • Directoryservices
        • UserService.java
      • Directoryswagger
        • SwaggerCreateUser.java
      • UserCrudApplication.java
    • Directoryresources
      • application.properties

Let’s take a closer look to the components’ duties by zooming in into user_crud_server package:

  • Directorycontrollers
    • UserController.java exposes API endpoints, instantiates the service
  • Directorymodels
    • User.java database model
    • UserRequest.java API response model
  • Directoryservices
    • UserService.java business logic that implements CRUD operations
  • Directoryswagger
    • SwaggerCreateUser.java @interface annotation for Swagger UI’s POST API endpoint
    • all the other Swagger annotation interfaces
  • UserCrudApplication.java starting point of Spring Boot application
  1. Model
    A record class is a shallowly immutable, transparent carrier for a fixed set of values, called record components. Let’s define the user model as a java Record with the following record components: id, name and age.
  2. Service
    Defines how the CRUD operations are performed. Let’s use a .csv file to avoid implementing a whole database structure.
  3. Controller
    Expose APIs based on the Service methods.

If you don’t want to move away from my blog, here is the code!

User.java
package com.pietropoluzzi.user_crud_server.models;
public record User(Long id, String name, int age) {
}

Here is a list of API calls based on the OS you’re using.

POST

Windows PowerShell
curl -Method POST http://localhost:8080/users `
-Headers @{ "Content-Type" = "application/json" } `
-Body '{ "name": "John", "age": 18 }'

GET by ID

Windows PowerShell
curl -Method GET http://localhost:8080/users/1

GET all

Windows PowerShell
curl -Method GET http://localhost:8080/users

UPDATE by ID

Windows PowerShell
curl -Method PUT http://localhost:8080/users/1 `
-Headers @{ "Content-Type" = "application/json" } `
-Body '{ "name": "New Name", "age": 18 }'

DELETE by ID

Windows PowerShell
curl -Method DELETE http://localhost:8080/users/1

Add the following dependency to the pom.xml file in order to enable Swagger UI:

pom.xml
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.5.0</version>
</dependency>

Then run the project and take a look at localhost:8080/swagger-ui/index.html page.

Let’s create a utility record class called UserRequest.java to be used within Swagger annotations. It has the same parameters as User.java except for the id (which is auto-generated by UserService.java class):

UserRequest.java
package com.pietropoluzzi.user_crud_server.models;
import io.swagger.v3.oas.annotations.media.Schema;
@Schema(description = "Payload for creating a new user")
public record UserRequest(
@Schema(description = "User name", example = "John") String name,
@Schema(description = "User age", example = "24") Integer age) {
}

@RequestBody annotation from io.swagger.v3.oas.annotations.parameters conflicts with @RequestBody annotation from org.springframework.web.bind.annotation.RequestBody.

The most straightforward solution is to use fully-qualified name for Swagger’s @RequestBody.

UserController.java
package com.pietropoluzzi.user_crud_server.controllers;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
// DO NOT import io.swagger.v3.oas.annotations.parameters.RequestBody
@RestController
@RequestMapping("/users")
public class UserController {
// ...
@Operation(
summary = "Create a new user",
description = "Add a new user to the database.",
requestBody = @io.swagger.v3.oas.annotations.parameters.RequestBody(
required = true,
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = UserRequest.class)
)
)
)
@PostMapping
public User createUser(@RequestBody User user) {
return service.save(user);
}
}

To avoid writing controllers with hundreds of lines of code dedicated to Swagger annotations, you can create a package where to put custom interfaces for each REST API method. Then, just annotate the API methods within the controller with the custom interfaces.

Let’s take the POST method that creates a new user. The following code snippets shows both UserController implementation and SwaggerCreateUser interface:

UserController.java
package com.pietropoluzzi.user_crud_server.controllers;
// import ...
@RestController
@RequestMapping("/users")
public class UserController {
@SwaggerCreateUser
@PostMapping
public ResponseEntity<?> createUser(@RequestBody User user) {
/* method implementation */
}
/* all the other CRUD endpoints */
}