Temiz Bir RESTful API Tasarımı için 13 Best-Practice

Doğru bir API tasarımı, kullanım kolaylığı, çabuk anlaşılabilirlik, hata tamirinde hız ve kararlılığa katkı sağlar. Ayrıca API arkasındaki aplikasyonun doğru bir yapıyla inşa edilmesinde ve geliştirilmesinde geliştiriciyi olumlu yönlendirir.

Bu maddelerin önem sırasına göre dizilmediğini bildirmeliyim. Bence hepsi birbirini tamamlayıcı pratikler.

  1. İsim kullan, fiil kullanma

    REST (Temsili Durum Aktarımı) kaynak yönelimlidir (resource-oriented). Ve bir kaynak URI ile ifade edilir. Dolayısıyla kaynak isimle ifade edilir. Fiil zaten metottur. RESTful’da end-point ise fiil ve isimin bir kombinasyonu ile ifade edilir:

    Resource: /cars
    Method: GET
    End-point: GET /cars
    İş: Araba listesi döndürür
    
    Resource: /cars
    Method: POST
    End-point: POST /cars
    İş: Araba oluşturur
    
    Resource: /cars
    Method: PUT
    End-point: PUT /cars
    İş: Arabaları topluca replace eder
    
    Resource: /cars
    Method: PATCH
    End-point: PATCH /cars
    İş: Arabaları topluca kısmi günceller
    
    Resource: /cars
    Method: DELETE
    End-point: DELETE /cars
    İş: Arabaların hepsini siler
    
    Resource: /cars/123
    Method: GET
    End-point: GET /cars/123
    İş: ID'si 123 olan arabayı döndürür
    
    Resource: /cars/123
    Method: POST
    End-point: POST /cars/123
    İş: Method Not Allowed 405 hatası döndürür
    
    Resource: /cars/123
    Method: PUT
    End-point: PUT /cars/123
    İş: ID'si 123 olan arabayı replace eder
    
    Resource: /cars/123
    Method: PATCH
    End-point: PATCH /cars/123
    İş: ID'si 123 olan arabayı kısmi günceller
    
    Resource: /cars/123
    Method: DELETE
    End-point: DELETE /cars/123
    İş: ID'si 123 olan arabayı siler
    

    Resource yolu olarak fiil kullanmayın. Şunlar gibi:

    /getAllCars
    /createNewCar
    /deleteAllBlackCars
    

    Anlaşıldığı üzre metotlar yani fiiller CRUD işlemlerine karşılık geliyor: GET oku‘ya, POST oluştur‘a, PUT güncelle‘ye ya da değiştir‘e, PATCH kısmi güncelle‘ye, DELETE sil‘e denktir.

  2. GET metodu ve query parametreleri nesne veya nesnelerin state’ini değiştirmemeli
    Nesnelerin state’ini değiştirmek için POST, PUT, PATCH veya DELETE kullanın.

    Örneğin şunları kullanmayın:

    GET /cars/123?activate
    GET /cars/123/activate
    
  3. Yalnız çoğul isim kullan
    Tüm resource’lar için şunlarda olduğu gibi yalnızca çoğul isim kullanın:
    /cars/123 ( /car/123 değil)
    /users ( /user değil)
    /settings/sending-notification ( /setting/sending-notification değil)
    
  4. Bir resource eğer başka bir resource ile ilişkili ise sub-resource düzenini kullan
    Resource: /cars/123/drivers
    Method: GET
    İş: ID'si 123 olan arabanın sürücü listesini döndürür
    
    Resource: /cars/123/drivers/456
    Method: GET
    İş: ID'si 123 olan arabanın sürücülerinden ID'si 456 olanı döndürür
    
  5. Döndürülmesi istenen içerik tipini header bölümünde iste ve döndürülecek içeriğin tipini header bölümünde belirt

    İstemci de sunucu da hangi format’ta iletişim kurması gerektiğini bilmelidir. Bu bilgi HTTP header’ları ile aktarılmalıdır. Content-Type header’ı request format’ını tanımlamak için; Accept header’ı response format’ını veya format listesini bildirmek için kullanılmalıdır.

  6. Serializasyon formatı için HTTP header’larını kullan

    Serialize çıktı yapısı bir ya da birden fazla parametreye bağlı olacaksa bu bir header unsuru olarak bildirilmelidir. Örneğin:

    X-Serialization-Groups: Details,DriverDetails
    
  7. HATEOAS kullan

    Hypermedia as the Engine of Application State, daha iyi API navigasyonu sağlayacak olan bir hypertext link kullanımı prensibidir. Örneğin:

    {
      "id": 123,
      "manufacturer": "BMC",
      "drivers": [
        {
          "id": 456,
          "name": "Abdullah Pazarbasi",
          "links": [
            {
              "rel": "self",
              "href": "/api/v2/cars/123/drivers/456"
            }
          ]
        }
      ]
    }
    
  8. Koleksiyonlar için alan seçimi, filtreleme, sıralama ve sayfalama bilgisi sağla
    Alan Seçimi:

    Bir fields query parametresinin içinde virgülle ayrılmış alan isimlerini göndererek seçilen alanlar haricindekilerin değerlerinin null olarak dönmesini sağlayabilirsiniz. Bazen iletişim yükünü azaltmak için işe yarayabilir. Örneğin:

    GET /cars/123?fields=id,manufacturer,model
    
    Filtreleme:

    Süzme için şu örnekteki gibi parametre gönderebilirsiniz:

    GET /cars?color=black&classification=sport
    
    Sıralama:

    Artan ve azalan sıralama bildirmek için birden fazla alan için bile olsa şunun gibi kullanabilirsiniz:

    GET /cars?sort=-manufacturer,+model
    
    Sayfalama:

    offset ve limit parametreleri ile sayfalama sağlayabilirsiniz. Örneğin:

    GET /cars?offset=0&limit=10
    

    total count’u istemciye bildirmek için X-Total-Count header’ını kullanın.

  9. Versiyonla

    Şunun gibi resource yolu öncesi versiyon bildirin:

    GET /api/v2/cars/123
    

    Dikkat edilmesi gereken şeyler version başına v karakteri koymak ve versiyon numarasında nokta ( . ) kullanmamak.

  10. Hataları, HTTP Status Code’ları ile ele al

    Tüm status code’larını kullanmamıza gerek yok. Şunları kullanın:

    200 - OK : Herşey yolunda
    201 - Created : Yeni bir resource oluşturuldu
    202 - Accepted : Güncelleme isteği kabul edildi ve işlenecek
    204 - No Content : Resource listesi boş / Resource silindi
    304 - Not Modified : İstemci önbelleklenen veriyi kullanabilir
    

    4xx hataları error-payload’unda açıklanmalıdır.

    400 - Bad Request : Geçersiz request gövdesi / Eksik parametre / Geçersiz parametre
    401 - Unauthorized : User authentication gerekiyor
    403 - Forbidden : Sunucu talebinizi algıladı ve fakat reddetti / Sunucu talebinizi algıladı ve fakat talebi yapmaya yetkiniz yok
    404 - Not Found : URI ardındaki tekil resource mevcut değil
    405 - Method Not Allowed : Geçerli HTTP metodu söz konusu URI için kullanılamaz
    406 - Not Acceptable : İfade biçimi kabul edilemez durumda
    409 - Conflict : Birbiri içinde çelişki bulunan parametre seti alınmış
    415 - Unsupported Media Type : Desteklenmeyen içerik tipi / Desteklenmeyen request/response gövde notasyonu (json, xml vb.)
    422 - Unprocessable Entity : Ön koşulları gerçekleşmemiş olduğu için hedeflenen state değişimi gerçekleştirilemedi
    

    5xx hatalarının detayları production ortamında istemiciye ulaştırılmamalı ve fakat log’lanmalıdır.

    Burada sıralanan HTTP status’ları arasında yanlış anlaşılabilenler var.

    Her başarılı işleme cevap olarak 200 OK dönülmemeli. İşlemin türüne göre ayrım yapılmalıdır.

    202 Accepted, bir PUT talebinin veya bir PATCH talebinin anında gerçekleşmesi sonucu döndürülmemesi gerekir. 202 Accepted talebin geçerli olduğunu ve gerçekleştirilmek üzere kuyruğa alındığını bildirmek içindir. Başarılı bir PUT ya da PATCH sonucunda 200 OK dönülmelidir.

    Bir hata sonucunda meydana gelmemiş bir boş liste döndürülecekse 204 No Content ile döndürülmelidir. Böyle bir durumda 200 OK ya da 404 Not found kullanılmamalıdır. 404 Not Found tekil bir resource’un bulunamaması durumunda kullanılmalıdır.

    Başarılı bir DELETE işlemi sonucu ister tekil resource için olsun ister birden fazla resource için olsun 204 No Content döndürülmelidir.

    İsimlerin çağrıştırdığının tersi olarak 401 Unauthorized “unauthenticated” durumu, 403 Forbidden ise “unauthorized” durumu için kullanılmalıdır.

    406 Not Acceptable, 202 Accepted‘ın aksi olarak kullanılmalıdır.

    Peki error-payload ne sunmalı? Şu şekilde düzenlenebilir:

    {
      "errors": [
        {
          "userMessage": "Foo",
          "internalMessage": "Bar",
          "code": "E123",
          "moreInfo": "https://services.example.com/api/v2/errors/e123"
        }
      ]
    }
    
  11. HTTP Metodu Override’ına izin ver

    Bazı proxy’ler yalnız GET ve POST metodlarını destekler. Aktüel metod GET ya da POST olsa da X-HTTP-Method-Override header’ının değeri kastedilen metod olarak verilip engel kaldırılabilir.

  12. URI’ın sonundaki taksim işaretini kızmadan ele al
    GET /cars/123/
    

    Burada görüldüğü gibi sonuna fazladan taksim işareti konmuş. Böyle bir talebi kızarak geri çevirmeyip 301 kalıcı yönlendirmesi ile karşılayın.

  13. Tekil resource ve sub-resource’ların URI’daki karşılıkları için kullanılabilecek regular expression’lar (PCRE)

    Sayısal ID’ler için:

    ^[1-9]\d*$
    

    UUID’ler için:

    ^[\da-f]{8}\-[\da-f]{4}\-[1-5][\da-f]{3}\-[89ab][\da-f]{3}\-[\da-f]{12}$
    

    Slug name’ler için:

    ^[a-z\d]+(?:\-[a-z\d]+)*$