Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 3

Building REST APIs with Spring

Recommended resources

Hello! Welcome back to our course on building a production-grade rate limiter.

In our last lesson, we dove into the core of the Spring framework, understanding how it manages components through Dependency Injection and Spring Beans. You now know how to declare a class as a Spring-managed bean (e.g., with @Service) and how to inject it into another component using constructor injection.

Today, we'll put that knowledge into practice by building the "front door" to our application: a web-facing API. We will create the actual HTTP endpoints that users will interact with, and which our rate limiter will eventually protect.

Your learning outcome for this lesson:

Create REST API endpoints using @RestController and @RequestMapping annotations.



1. The Role of a Controller

In a web application, some component must be responsible for listening to incoming HTTP requests, processing them, and sending back a response. In Spring, this component is the controller.

For the REST APIs we're building, we'll use a specialized type of controller that returns data (like JSON) directly to the client, rather than rendering a user interface on the server.

The diagram below shows where a REST controller fits into a typical application architecture. It's the entry point that receives requests from a client.

Client-Application-Database Interaction for REST APIs
This diagram shows the flow of a request from a client to a REST Controller within the application. The controller processes the request and sends back an HTTP response.

2. Creating a REST Controller with @RestController

To create a REST controller in Spring Boot, you use the @RestController annotation. As we learned in the last lesson, this is a stereotype annotation, similar to @Component or @Service. It tells the Spring IoC container two things:

  1. This class is a Spring Bean and should be managed by the container.
  2. This class is a web controller, and its methods will handle incoming HTTP requests.

A key feature of @RestController is that it combines two other annotations: @Controller and @ResponseBody.

Composition of @RestController
This visual representation shows that `@RestController` is a convenience annotation that bundles the functionality of `@Controller` and `@ResponseBody`.

The @ResponseBody part is crucial: it instructs Spring to automatically serialize the object returned from your method into a suitable format (by default, JSON) and write it directly into the body of the HTTP response. This frees you from manually converting your Java objects to JSON strings.

To see a quick walkthrough of this concept, please watch the following video.

Spring & Spring Boot Annotations Series - #12 - @RestController Annotation

The video 'Spring & Spring Boot Annotations Series - #12 - @RestController Annotation' from Java Guides clearly explains the role of @RestController and how it simplifies the creation of RESTful web services.

Watch from the beginning to 03:04. Pay close attention to how @RestController replaces the need to use both @Controller and @ResponseBody on every method.


3. Mapping Requests with @RequestMapping

Now that you have a controller class, you need to specify which method should handle which request. For example, a GET request to /api/v1/status should be handled differently from a POST request to /api/v1/users.

This is done using the @RequestMapping annotation. You can use it at both the class level and the method level:

  • Class-level: Defines a base path for all endpoints within that controller. This is excellent for versioning and grouping your API (e.g., @RequestMapping("/api/v1")).
  • Method-level: Defines the specific path and HTTP method for a single endpoint.

@RequestMapping has several useful attributes, but the most common are path (or value) and method. Because specifying the HTTP method is so common, Spring provides convenient shortcut annotations that are more readable:

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping

These are the annotations you will use most often. For instance, @GetMapping("/users") is simply a shorthand for @RequestMapping(path = "/users", method = RequestMethod.GET).

The following video provides a great overview of how to use @RequestMapping and its various attributes.

Spring & Spring Boot Annotations Series - #13 - @RequestMapping Annotation

This video, '#13 - @RequestMapping Annotation' from the same Java Guides series, covers how to map requests to your controller methods.

Watch the following sections: Introduction (00:00 - 00:24): A quick overview of the annotation's purpose. Class-Level Mapping (00:58 - 03:06): Learn how to define a base URI for your controller. Specifying HTTP Methods (04:47 - 06:18): See how to define which HTTP verb an endpoint responds to.


4. Practical Exercise: Build Your First API Endpoint

It's time to apply these concepts in your project. We will follow the official Spring guide to build a simple "Hello, World" REST service. This will create the first tangible piece of our application, which we will later secure with our rate limiter.

The guide is an excellent, self-contained walkthrough. I'll break it down into steps for you.

Building a RESTful Web Service

The official Spring guide 'Building a RESTful Web Service' is the definitive starting point for creating APIs with Spring Boot. We will follow its structure to build our first controller.

You'll be working with this guide for the rest of this section. For now, just have it open and ready. I will direct you to the specific parts as we go.

Step 1: Create the Resource Representation Class

First, we need a Java class that defines the structure of the JSON data we will send back. In modern Java, a record is a perfect and concise way to do this.

Following the section "Create a Resource Representation Class" in the guide, create the Greeting record in your project. Place it in a new package, for example, com.example.ratelimiter.dto (Data Transfer Object).

package com.example.ratelimiter.dto;

public record Greeting(long id, String content) { }

Spring's web starter includes the Jackson library, which will automatically convert instances of this Greeting record into JSON format for the HTTP response.

Step 2: Create the Resource Controller

Now, create the controller that will handle requests and return a Greeting.

Following the section "Create a Resource Controller" in the guide, create the GreetingController class. Place it in a new package com.example.ratelimiter.controller.

Let's analyze the code from the guide:

package com.example.ratelimiter.controller;

import java.util.concurrent.atomic.AtomicLong;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.example.ratelimiter.dto.Greeting; // Make sure to import your Greeting record

@RestController
public class GreetingController {

    private static final String template = "Hello, %s!";
    private final AtomicLong counter = new AtomicLong();

    @GetMapping("/greeting")
    public Greeting greeting(@RequestParam(value = "name", defaultValue = "World") String name) {
        return new Greeting(counter.incrementAndGet(), String.format(template, name));
    }
}

Key points to notice:

  • @RestController: Marks this class as a request handler.
  • @GetMapping("/greeting"): Maps HTTP GET requests for /greeting to the greeting() method.
  • @RequestParam: Binds the value of the URL query parameter name to the name method parameter. If the name parameter is missing from the URL, it will use the defaultValue "World".
  • The method returns a Greeting object. Spring and Jackson handle the conversion to JSON automatically.

Step 3: Run and Test the Service

Your main application class, created by Spring Initializr, is already set up to scan for components like our new @RestController.

Run your application's main method from your IDE. Once it starts, you can test your new endpoint.

Follow the instructions in the "Test the Service" section of the guide.

  1. Open your web browser or an API client like Postman and navigate to http://localhost:8080/greeting. You should see:
    {"id":1,"content":"Hello, World!"}
    
  2. Now, try it with the query parameter: http://localhost:8080/greeting?name=User. You should see:
    {"id":2,"content":"Hello, User!"}
    

You have successfully created and tested your first REST API endpoint with Spring Boot!



Key Takeaways

  • A controller is a Spring-managed component that handles incoming web requests.
  • The @RestController annotation marks a class as a controller and ensures its methods' return values are written directly to the HTTP response body, typically as JSON.
  • The @RequestMapping annotation and its shortcuts (@GetMapping, @PostMapping, etc.) are used to map specific URL paths and HTTP methods to your controller methods.
  • You can use @RequestParam to easily access URL query parameters in your endpoint methods.

Next Steps

You've built a working API endpoint. However, a real-world API needs to do more than just send back data with a default "200 OK" status. For our rate limiter, we'll critically need to send a "429 Too Many Requests" status when a client exceeds their limit.

In the next lesson, we will focus on handling HTTP requests and responses in more detail, including how to return different HTTP status codes to the client. This will be a vital skill for implementing the core logic of our rate limiter.