Step 4
Step 4 — REST API design
30 min
Step 4 — REST API design
REST = resources as URLs, actions as HTTP verbs.
Resource = URL, action = verb
| URL | GET | POST | PUT | DELETE |
|---|---|---|---|---|
/api/posts |
list | create | — | — |
/api/posts/{id} |
one | — | replace | delete |
/api/posts/{id}/comments |
comments | new comment | — | — |
Resources are plural nouns (/posts). Don't put verbs in URLs (/getPost is wrong).
A controller in one page
@RestController
@RequestMapping("/api/posts")
@RequiredArgsConstructor
public class PostController {
private final PostService postService;
@GetMapping
public List<PostDto> list(@RequestParam(defaultValue = "20") int limit) {
return postService.findRecent(limit);
}
@GetMapping("/{id}")
public PostDto get(@PathVariable Long id) { return postService.findById(id); }
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public PostDto create(@Valid @RequestBody PostCreateRequest request) {
return postService.create(request);
}
@DeleteMapping("/{id}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void delete(@PathVariable Long id) { postService.delete(id); }
}
@Valid + @RequestBody validates input.
5 status codes you'll use
200 OK— read / update succeeded201 Created— created new204 No Content— deleted, no body400 Bad Request— invalid input404 Not Found— resource missing
DTOs vs entities — keep them separate
public record PostCreateRequest(
@NotBlank @Size(max = 200) String title,
@NotBlank String body
) {}
public record PostDto(Long id, String title, String body, Instant createdAt) {}
Entity = DB shape. DTO = API shape. Decouple them so changes don't ripple.
Try it
Implement PostService.findRecent(limit) using JpaRepository.findAll(PageRequest.of(0, limit, Sort.by("createdAt").descending())). Test with curl http://localhost:8080/api/posts.
Going deeper
Next
Step 5 adds JWT authentication.