REST API (Representational State Transfer) to styl architektoniczny oparty na protokole HTTP. Nie jest to standard ani protokół, lecz zbiór dobrych praktyk pozwalający na komunikację między klientem a serwerem.
Podstawowe zasady REST:
- Bezstanowość (Stateless): Każde zapytanie musi zawierać wszystkie informacje potrzebne do jego zrozumienia (brak sesji po stronie serwera).
- Zasoby (Resources): Wszystko jest zasobem (np. użytkownik, zamówienie)
✘ /getUser, /saveOrder✓ /users, /orders
- Standardowe metody HTTP: Wykorzystanie metod zgodnie z ich przeznaczeniem (GET, POST, PUT, DELETE).
– GET – pobieranie danych– POST – tworzenie zasobu– PUT/PATCH – aktualizacja– DELETE – usuwanie
- Jednolity interfejs: Spójność adresów URL i formatu danych (najczęściej JSON).
Zależności i podstawowe adnotacje
Do tworzenia API wystarczy zależność:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Kluczowe adnotacje:
@RestController: Łączy @Controller i @ResponseBody. Oznacza, że klasa obsługuje zapytania REST, a zwracane obiekty są automatycznie serializowane do JSON.
@RequestMapping: Definiuje bazową ścieżkę (np. /api/v1/users).
@GetMapping, @PostMapping, @PutMapping, @DeleteMapping: Specyficzne warianty metod HTTP.
@RequestBody: Mapuje body zapytania (JSON) na obiekt Javy.
@PathVariable: Wyciąga dane ze ścieżki (np. /users/{id}).
@RequestParam: Obsługuje parametry w URL (np. ?page=1).
Podział projektu:
W architekturze Spring Boot stosuje się podział na warstwy (Separation of Concerns), co pozwala na izolację kontraktu API od logiki biznesowej i bazy danych. W kontekście REST API najważniejszym elementem jest warstwa kontrolera (Web Layer).
Kontroler stanowi punkt wejścia do aplikacji. Odpowiada wyłącznie za:
- Obsługę protokołu HTTP (mapowanie ścieżek, metod i parametrów).
- Wstępną walidację danych wejściowych.
- Przekazywanie wywołań do warstwy serwisowej.
- Zwracanie odpowiedzi z odpowiednim statusem HTTP.
Cała złożoność aplikacji (warstwa logiki biznesowej oraz komunikacji z bazą) zostaje przeniesiona poza kontroler.
Przykład implementacji:
@RestController
@RequestMapping("/api/v1/books")
public class BookController {
private final BookService bookService;
public BookController(BookService bookService) { ... }
@GetMapping("/{id}")
public ResponseEntity<BookResponse> getBook(@PathVariable Long id) {
return ResponseEntity.ok(bookService.findById(id));
}
}
DTO (Data Transfer Object)
DTO służy do przenoszenia danych pomiędzy klientem a API.
Oddziela kontrakt REST od modelu domenowego i bazy danych. Mówiąc prosto, w odpowiedzi przesyłamy jedynie kontrolowany obiekt DTO, a nie całą encję z bazy danych wraz z ID czy danymi wrażliwymi.
Przykład:
public record BookResponse(
String ISBN,
String title
) {}
Odsyłamy odpowiedź z obiektem BookResponse który z całej encji Book (ID, isbn, title, createdAt, modifiedAt) wybiera jedynie dane potrzebne klientowi.
Statusy odpowiedzi
Poprawne użycie statusów HTTP jest kluczowe w REST API
Najbardziej podstawowe statusy:
200 OK – poprawne pobranie danych
201 Created – utworzenie zasobu
400 Bad Request – błąd walidacji
401 Unauthorized – dostęp nieautoryzowany
403 Forbidden – dostęp wzbroniony
404 Not Found – brak zasobu
409 Conflict – konflikt stanu
Obsługa błędów i walidacja
Uruchamiamy możliwość walidowania danych poprzez dodanie adnotacji:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Walidacja danych wejściowych poprzez Bean Validation:
@NotBlank
@Email
private String email;
@NotBlank
@Size(max=32)
private String name;
Walidacja uruchamiana jest przez adnotację @Valid:
public ResponseEntity<BookResponse> create(
@Valid @RequestBody CreateBookRequest request
) {}
- gdy postanowienia walidacji nie zostaną spełnione zostanie wyrzucony wyjątek.
- Globalna obsługa wyjątków pozwala zachować spójny format błędów:
@RestControllerAdvice
public class ApiExceptionHandler {
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<ApiError> handleValidation(MethodArgumentNotValidException ex) {
return ResponseEntity.badRequest()
.body(new ApiError("VALIDATION_ERROR", "Invalid request"));
}
}