Your First REST Controller
A REST API is a set of URLs that other programs call over HTTP to read and change data. A mobile app asks GET /api/players and receives JSON; a website sends POST /api/players with JSON to add one. Most Spring Boot projects in industry are exactly this: an API that a React, Angular or Android front end talks to.
In this lesson you will write a controller that returns text and JSON, see what Spring sends over the wire, and understand how a request travels from Tomcat to your method.
REST in one table
REST (Representational State Transfer) is a style, not a library. Its main ideas are that URLs name resources (things, as nouns) and HTTP methods say what to do with them:
| Method and URL | Meaning | Typical success status |
|---|---|---|
GET /api/players | List players | 200 OK |
GET /api/players/3 | Read player 3 | 200 OK |
POST /api/players | Create a player | 201 Created |
PUT /api/players/3 | Replace player 3 | 200 OK |
DELETE /api/players/3 | Delete player 3 | 204 No Content |
Data travels as JSON (JavaScript Object Notation), a text format every language can read. You will build all five endpoints over the next three lessons.
A controller that returns text and JSON
package in.cricket.academy.hello;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "Namaste from Pune Cricket Academy!";
}
@GetMapping("/academy")
public Academy academy() {
return new Academy("Pune Cricket Academy", "Pune", 1299, List.of("Under-14", "Under-19"));
}
public record Academy(String name, String city, int monthlyFee, List<String> batches) {}
}@RestControllermarks the class as a web controller whose method return values are written directly into the HTTP response body. It is@Controllerplus@ResponseBody.@GetMapping("/hello")mapsGET /helloto the method. There are matching@PostMapping,@PutMapping,@PatchMappingand@DeleteMappingannotations.- A
Stringreturn value is sent as plain text. Any other object, here a record, is converted to JSON by Jackson, the JSON library that the web starter includes.
Restart the app and call both endpoints. The -i option makes curl show the status line and headers too:
curl -i http://localhost:8080/helloHTTP/1.1 200 Content-Type: text/plain;charset=UTF-8 Content-Length: 34 Date: Sun, 01 Nov 2026 18:06:15 GMT Namaste from Pune Cricket Academy!
curl http://localhost:8080/academy{"name":"Pune Cricket Academy","city":"Pune","monthlyFee":1299,"batches":["Under-14","Under-19"]}Jackson used the record's component names as JSON keys and turned the List into a JSON array. You wrote no conversion code. Tomcat 11 leaves out the reason phrase ("OK") after the status code; the number is what matters.
How a request reaches your method
- Tomcat receives the HTTP request on port 8080.
- It hands the request to Spring's DispatcherServlet, the single front door of every Spring MVC application.
- The DispatcherServlet asks the handler mappings which method matches
GET /hello. They were built at startup by scanning every@RestController. - Your method runs and returns a value.
- An HTTP message converter turns the value into bytes: the string converter for
String, the Jackson converter for objects. It also setsContent-Type. - Tomcat sends the response.
If no mapping matches, Spring answers with its built-in error response:
curl -i http://localhost:8080/heloHTTP/1.1 404
Content-Type: application/json
Transfer-Encoding: chunked
Date: Sun, 01 Nov 2026 18:06:20 GMT
{"timestamp":"2026-11-01T18:06:20.521Z","status":404,"error":"Not Found","path":"/helo"}This JSON comes from Spring Boot's /error handling. In Exception Handling with ProblemDetail you will replace it with a standard format.
A controller for a resource
Real controllers group the endpoints for one resource under a common path with a class-level @RequestMapping, and get their data from a service through constructor injection:
package in.cricket.academy.player;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/players")
public class PlayerController {
private final PlayerService service;
public PlayerController(PlayerService service) {
this.service = service;
}
@GetMapping
public List<Player> list() {
return service.findAll();
}
}Player is a record (long id, String name, PlayerRole role, int age, String city, LocalDate joinedOn), and PlayerService keeps four sample players in a ConcurrentHashMap for now (a thread-safe map, because many requests run at the same time). Later lessons replace the map with a database without changing the controller.
curl http://localhost:8080/api/players[{"id":1,"name":"Aarav Patil","role":"BATTER","age":15,"city":"Pune","joinedOn":"2025-06-02"},{"id":2,"name":"Priya Deshmukh","role":"BOWLER","age":14,"city":"Pune","joinedOn":"2025-06-09"},{"id":3,"name":"Kabir Shaikh","role":"ALL_ROUNDER","age":16,"city":"Mumbai","joinedOn":"2025-07-14"},{"id":4,"name":"Meera Joshi","role":"WICKET_KEEPER","age":13,"city":"Nashik","joinedOn":"2026-01-05"}]Notice two Jackson defaults: an enum becomes its name ("BOWLER"), and a LocalDate becomes an ISO date string ("2025-06-09").
@Controller versus @RestController
@Controller is the older, more general annotation. Its methods return the name of a view (an HTML template) unless you add @ResponseBody. @RestController saves you from writing @ResponseBody on every method. Use @RestController for APIs and @Controller for server-rendered HTML pages, covered in the Thymeleaf lesson.
Common mistakes
- Using
@Controllerfor an API. Spring then looks for a template called"Namaste from..."and fails with an error about a missing view. Use@RestController. - Returning a class without getters or record components. Jackson serialises public getters and record components. A class with only private fields becomes
{}or fails. - Putting business logic in controllers. It cannot be reused or unit-tested easily; move it to a
@Service. - Forgetting to restart. Without DevTools, code changes need a restart of the application before
curlsees them.
Key points
@RestController=@Controller+@ResponseBody: return values become the response body.@GetMapping,@PostMapping,@PutMapping,@PatchMappingand@DeleteMappingmap HTTP methods and paths to methods.- Strings are sent as
text/plain; records and other objects become JSON through Jackson. - The DispatcherServlet routes each request to the matching method; unmatched URLs get Spring Boot's 404 error JSON.
- Use a class-level
@RequestMapping("/api/...")per resource and keep controllers thin.
Exercise
Add a GET /api/batches endpoint that returns a JSON array of batch objects for the academy. Each batch has a name, an ageLimit and a timing, for example Under-14 at "6:00 AM" and Under-19 at "7:30 AM". Use a record, write the controller, and show the expected curl output.
Show solution
@RestController
@RequestMapping("/api/batches")
public class BatchController {
public record Batch(String name, int ageLimit, String timing) {}
@GetMapping
public List<Batch> list() {
return List.of(
new Batch("Under-14", 14, "6:00 AM"),
new Batch("Under-19", 19, "7:30 AM"));
}
}curl http://localhost:8080/api/batches[{"name":"Under-14","ageLimit":14,"timing":"6:00 AM"},{"name":"Under-19","ageLimit":19,"timing":"7:30 AM"}]Jackson writes the record components in declaration order, and the list becomes a JSON array.
Quick Quiz
1. What does @RestController combine?
Every method's return value is written straight into the response body.
2. A controller method returns a record. What does the client receive?
The Jackson message converter serialises objects to JSON.
3. Which Spring component receives every request first and routes it to a controller method?
The DispatcherServlet is the front controller of Spring MVC.
4. Which HTTP method and status fit "create a new player"?
Creating a resource uses POST and usually answers 201 Created.
Learn Spring Boot with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
