REST APIs
REST APIs are how modern apps talk to each other. By the end of this lesson you'll build a real CRUD API with Spring Boot — mapping HTTP verbs to methods, returning correct status codes, validating input, and handling errors cleanly. This is one of the most marketable Java skills you can have.
Learn REST APIs in our free Java course — a beginner-friendly interactive lesson with worked examples, a practice exercise and a quick reference.
Part of the free Java course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.
What You'll Learn in This Lesson
1️⃣ REST in Plain English
A REST API exposes your data as resources living at URLs — /api/users is the whole collection, /api/users/42 is one user. You act on a resource with an HTTP verb , and the server replies with a status code plus (usually) JSON.
💡 Analogy: A REST API is like a restaurant. Each endpoint is a dish on the menu (a resource). You use different verbs to interact — GET (read the menu), POST (place an order), PUT (change your order), DELETE (cancel it). The waiter (the server) answers with a status code : 200 (here's your food), 201 (order created), 404 (we don't serve that), 500 (the kitchen caught fire).
The golden rule: the URL names the thing, the verb names the action . So /api/getUser?id=42 is wrong — the verb is hiding in the URL. The RESTful version is GET /api/users/42 .
Method
Endpoint
Action
Typical status
GET
/api/users
List all users
200 OK
/api/users/42
Get user #42
200 / 404
POST
Create a user
201 Created
PUT
Replace user #42
DELETE
Delete user #42
204 No Content
2️⃣ Your First @RestController (CRUD)
Mark a class @RestController and Spring treats its methods as web handlers that return JSON. Add @RequestMapping("/api/users") to set a shared URL prefix, then map each verb to a method: @GetMapping , @PostMapping , @PutMapping , @DeleteMapping .
Three annotations pull data out of the request: @PathVariable grabs the 42 from /api/users/42 , @RequestParam reads query parameters after the ? , and @RequestBody parses the JSON body of a POST/PUT into a Java object.
Wrap the return value in ResponseEntity<T> when the status code varies . Returning a user gives 200 OK ; ResponseEntity.status(HttpStatus.CREATED) gives 201 after a create; .notFound().build() gives 404 ; .noContent().build() gives 204 after a delete.
🎯 Your Turn #1 — GET One Resource (200 or 404)
Finish the three blanks: declare the path variable in the mapping, bind it from the URL, and return 200 OK with the body. The expected requests and responses are in the comments so you can check yourself.
3️⃣ The Service Layer and DTOs
A controller's only job is HTTP — read the request, call something, shape the response. The actual work (rules, calculations, talking to the database) belongs in a @Service class. Spring creates the service and passes it into the controller via constructor injection , so you never new it yourself. Thin controller + fat service is the pattern that keeps real apps testable.
Just as important: never return your database entity directly . Entities often carry sensitive fields — a password hash, internal flags — and serializing the whole thing leaks them to every client. Instead, map the entity to a DTO (Data Transfer Object): a small record holding only the fields the client is allowed to see.
4️⃣ Validation and Global Error Handling
Never trust client input. Put Bean Validation annotations on a request record to describe what "valid" means, then add @Valid to the @RequestBody parameter. Spring checks the rules before your method runs and automatically rejects bad input with 400 Bad Request .
@NotBlank — must not be null or empty/whitespace
@Size(min = 2, max = 50) — string length constraints
@Min(0) @Max(150) — numeric range (inclusive)
For everything else, centralize error handling in one @ControllerAdvice class. Each @ExceptionHandler method catches an exception type and turns it into a clean HTTP response — a validation failure into 400 , a missing resource into 404 — so every endpoint reports errors in the same JSON shape and your controllers stay focused on the happy path.
🎯 Your Turn #2 — POST That Returns 201 Created
Fill the three blanks: the annotation that handles POST, the one that reads the JSON body, and the status that means "created". The expected request and response are in the comments.
Common Errors (and the Fix)
- ❌ Always returning 200: sending 200 OK for a create, a delete, or a missing resource lies to the client and breaks tooling that keys off status codes. Fix: return 201 on create, 204 on delete, 404 when not found, 400 for bad input — use ResponseEntity to set them.
- ❌ No validation: trusting @RequestBody input lets empty names, junk emails, and absurd numbers straight into your data. Fix: annotate the request record ( @NotBlank , @Email , @Min / @Max ) and add @Valid to the body parameter so Spring returns 400 automatically.
- ❌ Exposing entities directly: returning your database entity serializes every field — including passwordHash or internal flags — and leaks it to clients. Fix: map the entity to a DTO that contains only the fields the client should see.
- ❌ No error handling: an uncaught exception falls through to Spring's default whitebox 500 with a stack trace, and clients get no usable message. Fix: add a @ControllerAdvice with @ExceptionHandler methods so each failure maps to a sensible status and a consistent JSON body.
- ❌ Verbs in the URL: POST /api/createUser or GET /api/getUser?id=42 duplicates the HTTP method in the path. Fix: let the verb carry the action — POST /api/users and GET /api/users/42 .
🧩 Mini-Challenge — A Notes API
Now write a small CRUD controller yourself from an outline. Build POST (201), GET-by-id (200 or 404), and DELETE (204) for a Note resource, using @RequestBody , @PathVariable , and ResponseEntity with the correct status codes. The expected requests and responses are in the comments.
📋 Quick Reference — HTTP Verbs & Annotations
Verb
Annotation
Purpose
Success status
@GetMapping
Read a resource or list
@PostMapping
Create a new resource
@PutMapping
Replace a resource
@DeleteMapping
Remove a resource
—
@PathVariable
Read a value from the URL path
/users/ { id }
@RequestParam
Read a query-string parameter
?role=admin
@RequestBody
Parse the JSON request body
POST/PUT
@Valid
Enforce Bean Validation rules
400 on failure
@ControllerAdvice
Global exception → HTTP mapping
404 / 400 / 500
❓ Frequently Asked Questions
🎉 Lesson Complete!
Great work! You can now build a production-quality REST API with Spring Boot: map verbs with @GetMapping / @PostMapping / @PutMapping / @DeleteMapping , read input with @PathVariable / @RequestParam / @RequestBody , return correct status codes with ResponseEntity , keep logic in a @Service , hide sensitive fields behind DTOs, validate with @Valid , and handle errors globally with @ControllerAdvice .
Next up: JSON & XML — the serialization formats your API uses to turn objects into the data clients receive.
Practice quiz
What is the RESTful URL for getting user #42?
- GET /api/getUser?id=42
- POST /api/users/42
- GET /api/users/42
- GET /api/user/get/42
Answer: GET /api/users/42. The URL names the resource and the verb names the action: GET /api/users/42. Verbs do not belong in the path.
Which annotation makes a class return JSON from every method automatically?
- @RestController
- @Controller
- @Service
- @Component
Answer: @RestController. @RestController is @Controller + @ResponseBody, so each method serializes its return value to JSON.
Which annotation reads the JSON payload of a POST request into a Java object?
- @PathVariable
- @RequestParam
- @ModelAttribute
- @RequestBody
Answer: @RequestBody. @RequestBody deserializes the JSON body of a POST/PUT into your record or class.
Which annotation pulls the 42 out of /api/users/42?
- @RequestParam
- @PathVariable
- @RequestBody
- @RequestHeader
Answer: @PathVariable. @PathVariable binds a value that is part of the URL path. @RequestParam reads query parameters after the '?'.
What status code should a successful POST that creates a resource return?
- 201 Created
- 200 OK
- 204 No Content
- 404 Not Found
Answer: 201 Created. Creating a new resource should return 201 Created, not a plain 200.
What status code is correct for a successful DELETE with no body to return?
- 200 OK
- 201 Created
- 204 No Content
- 400 Bad Request
Answer: 204 No Content. 204 No Content signals success with nothing to return — the right code after a delete.
Why return ResponseEntity<T> instead of the object directly?
- It is faster
- It lets you set the status code, headers, and whether there's a body
- It is required by Spring
- It automatically validates input
Answer: It lets you set the status code, headers, and whether there's a body. Returning the object always sends 200. ResponseEntity lets the status vary (201, 204, 404) and add headers.
Why map a database entity to a DTO before returning it?
- DTOs are faster to serialize
- Spring requires DTOs
- To make the entity immutable
- To avoid leaking sensitive fields like a password hash to clients
Answer: To avoid leaking sensitive fields like a password hash to clients. Returning the entity serializes every field. A DTO exposes only the fields the client is allowed to see.
How do you make Spring validate a request body before your method runs?
- Call validate() manually
- Add @Valid to the @RequestBody parameter (with Bean Validation annotations on the record)
- Wrap it in a try/catch
- Use @Controller instead of @RestController
Answer: Add @Valid to the @RequestBody parameter (with Bean Validation annotations on the record). @Valid on the @RequestBody triggers the Bean Validation rules (@NotBlank, @Email, @Min...); Spring returns 400 on failure.
What is @ControllerAdvice (or @RestControllerAdvice) used for?
- Injecting services
- Mapping URLs to methods
- One global place to turn exceptions into consistent HTTP error responses
- Defining database entities
Answer: One global place to turn exceptions into consistent HTTP error responses. @ControllerAdvice with @ExceptionHandler methods centralizes error handling so every endpoint returns errors in the same shape.
Continue this course
- Previous: Previous Lesson
- Next: Next Lesson