Building REST APIs
1. Creating REST Controllers (@RestController)
Example: CRUD REST controller
@RestController
@RequestMapping("/api/v1/products")
public class ProductController {
private final ProductService svc;
public ProductController(ProductService svc) { this.svc = svc; }
@GetMapping public List<ProductDto> list() { return svc.findAll(); }
@GetMapping("/{id}") public ProductDto byId(@PathVariable long id) { return svc.find(id); }
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ProductDto create(@Valid @RequestBody NewProduct body) { return svc.create(body); }
}
| Annotation | Effect |
|---|---|
@RestController |
@Controller + @ResponseBody |
@RequestMapping |
Class-level base path |
@CrossOrigin |
Per-controller CORS |
2. Mapping HTTP Requests (@RequestMapping)
| Attribute | Description |
|---|---|
path / value |
URI patterns (Ant-style) |
method |
HTTP verbs |
params |
Required/forbidden query params |
headers |
Conditional on headers |
consumes |
Content-Type constraint |
produces |
Accept constraint |
3. Using Specific HTTP Methods
| Annotation | HTTP Verb | Idempotent? |
|---|---|---|
@GetMapping |
GET | Yes |
@PostMapping |
POST | No |
@PutMapping |
PUT | Yes |
@PatchMapping |
PATCH | No |
@DeleteMapping |
DELETE | Yes |
4. Handling Path Variables (@PathVariable)
Example: Multiple path variables
@GetMapping("/users/{userId}/orders/{orderId}")
public OrderDto get(@PathVariable Long userId,
@PathVariable("orderId") UUID id) { /* … */ }
| Feature | Notes |
|---|---|
| Type conversion | Auto for primitives, UUID, enums |
required=false |
Optional path var (rare) |
| Regex constraint | {id:\d+} |
5. Handling Query Parameters (@RequestParam)
| Syntax | Behavior |
|---|---|
@RequestParam String q |
Required |
@RequestParam(required=false) String q |
Optional |
@RequestParam(defaultValue="10") int size |
Default |
@RequestParam List<String> tags |
Repeated / CSV params |
@RequestParam Map<String,String> all |
All query params |
6. Handling Request Body (@RequestBody)
Example: Validated request body with Location header
public record NewUser(@NotBlank String email, @Size(min=8) String password) {}
@PostMapping
public ResponseEntity<Void> create(@Valid @RequestBody NewUser body) {
long id = service.register(body);
return ResponseEntity.created(URI.create("/api/users/" + id)).build();
}
7. Returning Response Entity (ResponseEntity<T>)
| Builder | Use |
|---|---|
ResponseEntity.ok(body) |
200 with body |
ResponseEntity.status(201).body(x) |
Custom status |
ResponseEntity.created(uri).build() |
201 + Location |
ResponseEntity.noContent().build() |
204 |
ResponseEntity.notFound().build() |
404 |
.headers(h).cacheControl(cc) |
Header customization |
8. Setting Response Status (@ResponseStatus)
Example: @ResponseStatus on method and exception
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public ProductDto create(@RequestBody NewProduct p) { /* … */ }
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Order not found")
public class OrderNotFoundException extends RuntimeException {}
9. Handling HTTP Headers
| Element | Use |
|---|---|
@RequestHeader("X-API-Key") String key |
Single header |
@RequestHeader Map<String,String> |
All headers |
@RequestHeader HttpHeaders headers |
Typed |
ResponseEntity.ok().header("X-Total", "42") |
Set response header |
10. Using Content Negotiation
| Strategy | Configuration |
|---|---|
| Accept header | Default — most RESTful |
| Path extension | Disabled by default since 5.3 |
| Query param | spring.mvc.contentnegotiation.favor-parameter=true |
| Producible types | @GetMapping(produces={"application/json","application/xml"}) |
11. Implementing HATEOAS Links
Example: HATEOAS self and collection links
@GetMapping("/{id}")
public EntityModel<Order> one(@PathVariable Long id) {
Order o = repo.findById(id).orElseThrow();
return EntityModel.of(o,
linkTo(methodOn(OrderController.class).one(id)).withSelfRel(),
linkTo(methodOn(OrderController.class).all()).withRel("orders"));
}
| API | Use |
|---|---|
EntityModel<T> |
Single resource + links |
CollectionModel<T> |
Collection + links |
RepresentationModelAssembler |
Reusable assembler |
12. Versioning REST APIs
| Strategy | Example | Trade-off |
|---|---|---|
| URI path | /api/v1/users |
Simple, cache-friendly |
| Query param | ?v=2 |
Easy but ugly |
| Header | X-API-Version: 2 |
Clean URLs, less discoverable |
| Media type | application/vnd.api.v2+json |
True REST, harder for clients |