💻

Creating a Simple REST Endpoint with Spring Boot

Published on

In this post I want to demonstrate how easy it is to create a REST endpoint with Spring Boot and to demonstrate it we will create a simple REST endpoint for our fridge 🧊. It will be a CRUD endpoint that will allow listing creating, updating and deleting products.

The full source code for this post is available in my lab repository:

Bootstrapping (Generating) a New Application

The first thing we need to do to create a REST endpoint using Spring Boot is to boostrap a new application. This can be done using Spring Initializr and I covered this in my previous post, which you can find here - How to Create a New Application with Spring Boot.

Many IDEs use Spring Initializr to generate a new application, here is how I did it in IntelliJ Idea:

Creating a REST Endpoint with @RestController

To create a REST endpoint we need to create a Java class and annotate it with @RestController. It is important that this class is created in the same package or sub-package of the main application class. If it is not, Spring Boot will not locate this class automatically and will need extra configuration to find it.

package com.example.fridgeservice;

import org.springframework.web.bind.annotation.*;

@RestController
public class FridgeController {
}

@RestController annotation tells Spring that this class is special and will respond to requests. As its name suggests, this class is a web @Controller, so Spring considers it when handling incoming web requests.

Once we have our RestController, the only thing that is left to finish our implementation is to add methods that will respond to different HTTP requests and mark them with special Spring annotations.

Specifying a Base Path for the REST Endpoint with @RequestMappings

In the controller we will create methods that can be accessed by a specific path and request method. If we know ahead of time that all of our endpoints are going to have the same base path, we can create that path at the class level using the @RequestMapping annotation:

package com.example.fridgeservice;

import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("fridge") // translates to http://localhost/fridge
public class FridgeController {
}

This tells Spring that any mapping created in this controller will start with the path /fridge unless overridden.

Before we create methods in our controller that can respond to requests, we will need some sample data to work with. This data could come from a file, database, or a simple in-memory storage.

In this example, we declare a new instance variable called storedProducts that will hold a List<String>. It will be initialized it with some products in constructor:

package com.example.fridgeservice;

import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("fridge")
public class FridgeController {

    private List<String> storedProducts;

    public FridgeController() {
        storedProducts = new ArrayList<>();
        storedProducts.add("Milk");
        storedProducts.add("Pizza");
        storedProducts.add("Mineral Water");
    }

    // list

    // add

    // replace

    // take

}

Let's add methods that will respond to our HTTP requests.

HTTP GET Mapping via @GetMapping

The first method will return a list of all products stored in our fridge:

    @GetMapping
    public List<String> list() {
        return storedProducts;
    }

@GetMapping tells Spring that this method will be accessed using the GET request. @GetMapping is a composed annotation that acts as a shortcut for @RequestMapping(method = RequestMethod.GET).

At this point we have an endpoint that responds to GET requests:

HTTP POST Mapping via @PostMapping

We need a way to add new products to our fridge. It makes sense to use POST request for that. Let's use the following JSON format as a payload:

{
  "productName": "Juice"
}

Here is the implementation:

    // add
    @PostMapping
    public void add(@RequestBody Map<String, String> payload) {
        storedProducts.add(payload.get("productName"));
    }

The @PostMapping annotation tells Spring that this method will accept a POST request to /fridge. The @RequestBody annotation tells Spring to map what is in the request body to the variable payload. Because JSON is key/value pair, a sensible data type is Map<String, String>. Finally, in the body of the method itself we will get a product name and add it to the list of stored products.

Let's verify it by issuing a POST request.

If your application is still running in the first terminal tab, stop it with Ctrl-C and re-run using the following command:

./mvnw spring-boot:run

Now we have a way of adding new products to our fridge.

HTTP PUT Mapping via @PutMapping

We can use a similar approach to replace stored products. Because we have a simple string representation for stored products, we simply pass the old product name and a new product name for replacement. This way our controller method can look up the current value and replace that object in the list. The JSON payload will look like this:

{
  "oldProductName": "Milk",
  "newProductName": "Beer"
}

My implementation:

    // replace
    @PutMapping
    public void replace(@RequestBody Map<String, String> payload) {
        String oldProduct = payload.get("oldProductName");
        String newProduct = payload.get("newProductName");
        if (storedProducts.contains(oldProduct)) {
            storedProducts.set(storedProducts.indexOf(oldProduct), newProduct);
        }
    }

The @PutMapping annotation tells Spring that this method will respond to PUT requests on /fridge. The @RequestBody annotation works exactly the same as it did on the add method.

Let's verify if it works.

If your application is still running in the first terminal tab, stop it with Ctrl-C and re-run using the following command:

./mvnw spring-boot:run

It works!

HTTP DELETE Mapping via @DeleteMapping

Finally, we will need to take a product from our fridge and DELETE request will serve for this purpose:

    // take
    @DeleteMapping
    public void take(@RequestParam String productName){
        storedProducts.remove(productName);
    }

The @DeleteMapping annotation tells Spring that this method will respond to DELETE requests on /fridge endpoint. In this scenario, we don't need to send a JSON request body, just a simple request parameter. When we want to take (delete) a product, we would send a DELETE request to a URL like /fridge?productName=Pizza. The @RequestParam will assign the request parameter to the variable productName.

Example:

Do not forget to restart the application after new changes!

The final code for our REST endpoint (the full source code can be found in my lab repository):

package com.example.fridgeservice;

import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * A simple CRUD endpoint to manage products in the fridge. It shows how easy it is to create a REST API with Spring Boot.
 */
@RestController
@RequestMapping("fridge")
public class FridgeController {

    private List<String> storedProducts;

    public FridgeController() {
        storedProducts = new ArrayList<>();
        storedProducts.add("Milk");
        storedProducts.add("Pizza");
        storedProducts.add("Mineral Water");
    }

    // list
    @GetMapping
    public List<String> list() {
        return storedProducts;
    }

    // add
    @PostMapping
    public void add(@RequestBody Map<String, String> payload) {
        storedProducts.add(payload.get("productName"));
    }

    // replace
    @PutMapping
    public void replace(@RequestBody Map<String, String> payload) {
        String oldProduct = payload.get("oldProductName");
        String newProduct = payload.get("newProductName");
        if (storedProducts.contains(oldProduct)) {
            storedProducts.set(storedProducts.indexOf(oldProduct), newProduct);
        }
    }

    // take
    @DeleteMapping
    public void take(@RequestParam String productName){
        storedProducts.remove(productName);
    }

}

Conclusion

As you can see it is very easy to create a REST endpoint with Spring Boot, you just need to use special animations and the rest (JSON serialization for example) will be handled by Spring behind the scenes.

Do not forget to Subscribe to my RSS feed for new updates!