REST API – POST vs PUT vs PATCH

Ten wpis sponsoruje literka P. P jak Protokół HTTP, który oferuje, między innymi, trzy metody używane przy tworzeniu przyzwoitego API typu REST: POST, PUT i PATCH . Przyjrzyjmy się ich funkcją różnicom między nimi.

Gdy implementujemy operacje z rodziny CRUD to pojawia się tam zazwyczaj POST i PUT. Pierwszy jako Create, a drugi jako Update.

POST

Post służy do tworzenia nowych obiektów domenowych. Dane, jakie są przesłane w ciele żądania HTTP są zazwyczaj bezpośrednio mapowane na obiekt Javowy i utrwalane w bazie danych.

W odpowiedzi powinniśmy wysłać status CREATED, albo OK wraz z informacjami o nowo stworzonym obiekcie, albo tylko o jego ID. Nie ma tu jednoznacznie, bez kontekstowo dobrych/złych odpowiedzi.

Żądania z metodą POST kierowane są na główny adres, gdzie dostępny jest dany zasób np. /guests

Przykładowa implementacja w Spring Framework

    @PostMapping("/guests")
    @ResponseStatus(HttpStatus.CREATED)
    public void createGuest(@RequestBody Guest guest) {
        guestService.createNewGuest(guest);
    }

I przykładowy request

$ curl -i -X POST --data '{"firstName":"Pawel","lastName":"Cwik","age":34}' -H "Content-Type: application/json" localhost:8080/api/guests

PUT

Put standardowo służy to aktualizacji bądź stworzenia nowego obiektu. Podobnie jak Post wymaga, by w ciele żądania znajdował się komplet danych umożliwiających utworzenie całego obiektu po stronie serwera. Jeśli takie obiekt już istnieje, to aktualizowane są wszystkie dane, jeśli nie to na podstawie przesłanych informacji tworzony jest nowy obiekt.

W odpowiedzi serwer powinien wysłać odpowiedź CREATED lub w przypadku aktualizacji, NO_CONTENT. Ponownie, czy wysyłać w odpowiedzi reprezentacje nowo utworzonego (lub zaktualizowanego) obiektu to już kwestia wyboru.

Żądania metody PUT kierowane są na adres z parametrem ścieżki określającym ID obiektu, który chcemy zaktualizować np. /guests/2. Należy pamiętać, że jeśli w naszym systemie nie znajduje się obiekt z ID 2, to nowo utworzony obiekt nie będzie miał z góry określonego ID na 2. To baza danych zazwyczaj odpowiada za nadawanie unikalnych identyfikatorów, nigdy użytkownik.

Przykładowa implementacja w Spring Framework

    @PutMapping("/guests/{id}")
    public ResponseEntity<Void> updateGuest(@PathVariable Long id, @RequestBody  Guest guest) {
        Long updatedId = this.guestService.fullUpdateGuest(id, guest);

        if(updatedId==id) {
            return ResponseEntity.noContent().build();
        } else {
            return ResponseEntity.created(URI.create("guests/"+updatedId)).build();
        }
    }

Oraz żądanie. Przy nieistniejącym obiekcie http_code będzie miał wartość 201, a przy aktualizacji istniejącego 204

curl -w " ---- RSP CODE: %{http_code}" -X PUT --data '{"firstName":"Pawel","lastName":"Cwik","age":37}' -H "Content-Type: application/json" localhost:8080/api/guests/2

PATCH

Metoda Patch podobnie jak Put służy do aktualizacji danych o obiekcie, jednak wymaga ona, by dany obiekt istniał. A to dlatego, że nie przesyła ona w żądaniu kompletu danych, a jedynie te dane, które mają zostać zaktualizowane.

W odpowiedzi powinniśmy wysyłać NO_CONTENT, bądź OK i w odpowiedzi wysłać dane o zaktualizowanym obiekcie.

Żądania typu PATCH kierowane są na adres wraz z ID obiektu, który chcemy zaktualizować np. /guests/2

Implementacja w Spring Framework

    @PatchMapping("/guests/{id}")
    @ResponseStatus(HttpStatus.NO_CONTENT)
    public void patchGuests(@RequestBody Map<String, Object> updates, @PathVariable Long id) {
        this.guestService.partialUpdate(id, updates);
    }

Przykładowe zapytanie

 curl -w " ---- RSP CODE: %{http_code}" -X PATCH --data '{"age":40}' -H "Content-Type: application/json" localhost:8080/api/guests/2     

Na koniec jeszcze mała uwaga w temacie odpowiedzi HTTP. Czy zwracamy tylko odpowiedni kod, czy ogólne OK, ale z dodatkiem reprezentacji nowo utworzonego/zaktualizowanego obiektu w ciele odpowiedzi. Nie ma jednoznacznych dobrych i złych praktyk.

Kiedy POST, Kiedy PATCH, Kiedy PUT

Skoro mamy trzy bardzo zbliżone rzeczy, to kiedy należy której metody użyć? Na dobrą sprawę PUT może nam załatwić i Update, i Create.

Kiedy chcemy tylko i wyłącznie utworzyć nowy obiekt używamy POST, jest to metoda dokładnie po to stworzona.

Kiedy chcemy zaktualizować obiekt, to używamy PUT. Czasem, by uniknąć “niejednoznaczności” blokuje się po stronie serwera możliwość tworzenia nowych obiektów za pomocą PUT, tak by używać go tylko i wyłącznie do aktualizacji stanu.

PATCH ma nieco większy stopień skomplikowania, zarówno po stronie UI jak i serwerowej. Powinno się go używać albo w momencie, gdy pracujemy na naprawdę bardzo dużych obiektach i różnica w czasie i zasobach między pełną aktualizacją a częściową jest odczuwalna, albo gdy zależy nam na ograniczeniu do minimum ilości danych przesyłanych przez sieć.

Kod aplikacji na GitHub.

Jeśli chcesz wiedzieć jak zintegrować API REST z UI stworzonym w ReactJS to zapraszam do dołączenia tutaj.

3 myśli w temacie “REST API – POST vs PUT vs PATCH”

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *