Rest API Tasarımı ve Best Practices

Gökhan Ayrancıoğlu
Delivery Hero Tech Hub
7 min readSep 27, 2021

Rest API tasarımı, web servisleri geliştirmenin en önemli kısmını içinde barındırmaktadır. Oluşturulan Rest API’ler herhangi bir mobil, web ya da başka servislerle API entegrasyonlarına hizmet edebilir. Dolayısıyla esnek ve olabildiğince güçlü bir yapıda olmaları gereklidir. Çünkü oluşturulan endpointler genelde dışarıya açılabilir ve daha sonra buradaki değişiklikler bağımlılığı olan tüm servis ve client’ları ya da kullanıcıları etkileyebilir. İlk başta api tasarımını doğru ve en iyi şekilde tasarlamak geliştiricinin işini kolaylaştırırken kaliteyi artırır.

Burada anlatılanlar bir stardart değil, rest mimarisinin en iyi uygulamalarını barındırmaktadır. Rest API haberleşmesinde büyük ölçüde her kesim tarafından tercih edilen JSON (JavaScript Object Notation) notasyonu kullanmanız önerilir.

REST’te veriyi temsil eden her nesneye resource adı verilir. Örneğin; resource kullanıcılar, şirketler ya da hayvanlar gibi nesneleri temsil edebilir. Collection‘lar ise resourceler bütünüdür. Örneğin; kullanıcılar collection iken kullanıcı resource olarak adlandırılabilir.

HTTP Metotları

Http metodu bir REST API için yapılan isteğin türünü tanımlar. GET, POST, PUT, PATCH ve DELETE gibi HTTP yöntemleri temel CRUD işlemlerini oluştur. Oluştur, Oku, Güncelle, Sil gibi temel işlemleri gerçekleştirmek için oldukça sık kullanılmaktadır. Bunlara ek olarak pek sık kullanılmayan COPY, PURGE, LINK, UNLINK vb. gibi metotlar da vardır. En sık başvurulan HTTP metotları ve neden kullanıldıklarını sıralarsak:

  • GET: Veriyi almak
  • POST: Yeni veri eklemek
  • PUT: Varolan veriyi güncellemek
  • PATCH: Verinin sadece istenilen kısmında güncelleme yapmak
  • DELETE: Veriyi silmek

API Endpoint Naming (İsimlendirme)

Rest API tasarımının en önemli kısmı API isimlendirmesi diyebiliriz. İlk olarak HTTP metotlarını hatırlatmamın nedeni API isimlendirirken oldukça önemli bir role sahip olmalarındandır. Yoksa birçoğumuz bütün HTTP metotlarıyla oldukça sık karşılaşabiliyoruz.

- Resource’lar “isim” olmalıdır.

Yapılan işlemin ne olduğunu kolayca anlayabilmemiz ve geliştiricilere bu konuda ışık tutabilmemiz için isimlendirme oldukça önemli bir rol oynar. Yapılan isimlendirme kesinlikle “fiil” olarak değil “isim” olarak tasarlanmalıdır. Çünkü isimler, fiillerin sahip olmadığı özelliklere/niteliklere sahiptir. createUser bir aksiyonu betimler, fazlasını değil. Ancak users bir isim olarak farklı ve birçok niteliğe sahip olabilir. İlerleyen örneklerde ne kadar esneklik sağladığını daha belirgin şekilde görebilirsiniz. REST’in en genel özelliklerinden biri, URI’lerde fiiller yerine isimlerin kullanılmasıdır.

https://gokhana.dev/createUser gibi fiil şeklinde kullanılmamalıdır. Bunun yerine https://gokhana.dev/users şeklinde kullanılması çok daha doğru bir kullanım şeklidir.

- Resource’lar çoğul olarak isimlendirmelidir.

Bir zorunluluk olmasa da genel kullanım ismin çoğul halidir. Nesnelerimiz tek resource barındırmadıkça çoğul kullanmamız önerilmektedir. Kullanıcılar için bir endpoint tasarladığımızı düşündüğümüzde/users örneğini vermiştik, user’ın tekil bir özelliği olduğunda bunu /users/{id}/address şeklinde tanımlayabiliriz.

- Path Variable Kullanım

Bir resource’u hedef göstermek/tanımlamak amacıyla kullanılır. Özellikle bir collection’a ait bir resource hakkında işlem yapmak istiyorsanız onu unique yapan değeri(id olabilir) path variable olarak kullanabilirsiniz. Filtrelemek, sıralamak gibi işlemlerde ise Query String kullanmaya özen gösterilmelidir.

http://gokhana.dev/users Kullanıcı listesi için
http://gokhana.dev/users/{id} Verilen id'ye sahip kullanıcı için

- Doğru HTTP metotlarıyla eşleştirmek.

HTTP metotlarının API tasarımında ne kadar önemli olduğunu vurgulamıştık ve şimdi ise isimlendirme ile ilişkisini ortaya koyalım. Kullanıcı resource’umuz için kullanıcılar API’inin tasarımına başlamıştık. Kullanıcı ve onun adresi ile ilgili işlemler yapmak istediğimizde oluşturacağımız en doğru API isimlendirmesi aşağıdaki gibi örneklendirilebilir.

  • GET /users: Kullanıcı listesini çekmek için
  • POST /users: Yeni bir kullanıcı eklemek için
  • PUT /users: Kullanıcının bilgilerini güncellemek için
  • GET /users/{id}/address: Kullanıcının adres bilgisini çekmek için
  • DELETE /users/{id}: Kullanıcıyı silmek için

- İsimlendirmenin tümünde küçük harfler kullanılmalıdır.

Zorunlu olmadıkça URI pathleri isimlendirilirken küçük harfler tercih edilmelidir.

- Hiyerarşik ilişkileri belirtmek için slash (/) kullanılmalıdır.

Slash’lar kullanarak resourcelar arasındaki hiyerarşiyi gösterebiliriz. Yukarıdaki örnekte görüldüğü gibi /users/{id}/address resource’u/users/{id} resource'unun ve /users collection’ının altında yer almaktadır.

- URI’ın en sonunda slash (/) kullanılmamalıdır.

URI’ın path’inin son karakteri olarak slash kullanmak anlamlı olmamakla birlikte karışıklığa yol açabilir. Bu nedenle kullanmamak daha iyi bir pratiktir.

- Boşluk veya birden fazla kelime için tire (-) kullanılmalıdır.

URI’lerin okunabilirliğini iyileştirmek için tire (-) kullanabilirsiniz.

URI’lerinizi daha kolay okunabilmesi ve yorumlanabilmesi için birden fazla kelimeden oluşan path’lerde tire (-) karakterini kullanabilirsiniz.

http://gokhana.dev/users/{id}/phone-number

Underscore kullanımından kaçınmamız gerekir. Underscore bazı tarayıcılarda veya farklı fontlarda karmaşaya neden olmaktadır. Genel kabul görmüş tire’yi kullanmak daha iyi bir tercih olacaktır.

- Dosya uzantıları kullanılmamalıdır.

Dosya uzantılarını URI’lerde barındırmak herhangi bir avantaj sağlamazken iyi bir görüntüde vermez, bunun yanı sıra URI’ın uzunluğunu kısaltır.

/users/{id}/orders.xml yerine /users/{id}/orders kullanılmalıdır.

- Http Header’lar kullanılmalıdır.

Request ve Response’ların özellikle hangi dil, hangi içerik için olduğu gibi bilgileri Http Header kısmında bildirebiliriz.

Accept-Language: tr-TR
Content-Type: application/json

- Collection’ları filtrelemek, sıralamak veya listelemek için query parametresi olarak belirtebiliriz.

Özellikle bir collection’ını belirli kural ya da filtrelere göre çekmek için query şeklinde parametrik olarak çekebiliriz.

http://gokhana.dev/users
http://gokhana.dev/users?gender=Female
http://gokhana.dev/users?gender=Female&sort=ASC
http://gokhana.dev/users?gender=Female&sort=ASC&page=1

Microservice Mimarisinde Rest API İsimlendirmesi

Anlatılan tüm kurallar microservice mimarisindeki web servislerimiz için de geçerlidir. Özellikle internal haberleşmelerde api isimlendirme karmaşası yaşanabiliyor. Bunu gidermek için önerim servis isimleriyle uri’yi başlatmak olabilir. Böylece onun resource’unu da belirtmiş oluruz. Bir IoT platformu kurguladığımızı düşünürseniz, mutlaka cihazları yöneten bir servis bulunur. Bu servise device-management adını verdiğimizde artık bu ismi de özellikle iç iletişim için API isimlendirmenin bir parçası yapabiliriz.

Robot süpürge bulunan evlerde -biz de olduğu gibi robota genelde “Katya” adı verildiğini düşünelim- bu robot’un bilgilerini çekmek isteyelim.

GET http://gokhana.dev/device-management/devices?name=Katya 

Versiyonlama

Web servisinizdeki Rest API’lerde önemli veya kullanıcıyı/geliştiriciyi etkileyecek değişikliklerde versiyonlama kullanmamak, API’lerinizi kullanan mevcut ürün veya hizmetlerin bozulmasına yol açabilir.

Şu an için tek bir versiyona sahip olunsa da, versiyonlamaya başlamak ileride oluşabilecek değişikliklere karşı size esneklik sağlayacaktır. Versiyonlar v harfi ile başlar ve yanına versiyon numarasını içerir. İlk versiyonunuz v1 şeklinde gösterilebilir. Kullanıcı resource’u için oluşturduğumuz endpoint’in son halini gösterebiliriz.

http://gokhana.dev/v1/users

Böylece kullanıcı resource’una ait Rest API’miz bir sürüme/versiyona sahip oldu. Herhangi bir büyük kırılma güncellemesi varsa, yeni API setini v2 şeklinde devam ettirebiliriz.

HTTP Response Kodları (HTTP Status)

Her istek başarılı veya başarısız bir response’la sonuçlanır. Http response kodları ise bu isteğin sonucunun ne olduğunu bildirmekle yükümlüdür. Bir web geliştirici iseniz halihazırda bunlara aşinasınızdır.

En çok kullanılanlara göz atıp farklı yaklaşımları ele alalım.

  • 200: OK, İşlem sorunsuz ve başarılı.GET /users/{id}
  • 201: Created, Yeni resource başarıyla oluşturuldu. |POST /users
  • 204: No Content, Resource boş/Resource silindi |DELETE /users/{id}
  • 400: Bad Request, Geçersiz request/Eksik ya da geçersiz query/parametre
  • 401: Unauthorized, Yetki/Authentication gerekiyor.
  • 403: Forbidden, Sunucuisteği reddetti ve isteğe yetkiniz yok
  • 404: Not found, İstekte bulunulan tekil resource mevcut değil
  • 405: Method Not Allowed: HTTP metodu yanlış
  • 409: Conflict: Uyumsuz istek, eski versiyondaki istek ya da Zaten varolan resource’u yeniden oluşturmaya çalışırken verilebilir.(Already exist)
  • 429: Too many requests, Çok fazla istekte bulunuldu.
  • 415: Unsupported Media Type: Desteklenmeyen ya da yanlış Content-Type
  • 5xx ise sunucu hatalarından oluşan HTTP response kodlarını içerir.

Rest API tasarımında her istek için doğru cevaplar almak çok önemlidir. API’yi kullanan geliştirici ve kullanıcılara doğru cevaplar vermek onları atacakları/yapacakları istek konusunda doğru bir şekilde yönlendirmeyi sağlar.

Not: Başka bir bakış açısı ise 200 ile birlikte hata durumlarını response body’sinde JSON olarak bildirmektir. Buradaki mantalite ise, Sunucunun işlemi başarıyla gerçekleştirdiği ve hatanın bilincinde olduğu ancak istenilenin teknik nedenler dışında bir durumdan dolayı gerçekleşmediğini 200 OK ile response body’sinde bildirilmesidir. Bu yönteminin yararı çoğu client kütüphanesinin 400'lü mesajları exception olarak kabul edip, exception olarak yönetmeye zorlamasıdır.

Response Modelleri

İsteklerde olduğu gibi response’larda da JSON kullanılmalıdır. Http Status kodlarıyla ilişkili response’lar gönderilmelidir.

{
"success": true,
"message": "Form values are not valid.",
"payload": {
"id": 12423,
"name": "Gökhan"
}
}

Hata detayları response body’sinin içinde yer almalıdır. Herhangi bir standart olmasa da aşağıdakine benzer yapılar kullanılabilir. Bazı error modellerinde code parametresi de geçerli olabiliyor. İşin özü response modeliniz olabildiğince açık ve API’lerinizi kullanan geliştiricilerin işine yarayacak ve kolayca kullanabilecekleri yapıda olmalıdır.

{
"success": false,
"message": "Form values are not valid.",
"payload": [
{
"field": "name",
"error": "name field can not be empty"
},
{
"field": "email",
"error": "Email is not valid."
}
]
}

API Olgunluk Kontrol Listesi

Rest yaklaşımının temellerini bir araya getirerek seviyelere ayıran Richardson Maturity Model (Leonard Richardson tarafından geliştirildi). Detaylarına Martin Fawler’ın bu yazısından ulaşabilirsiniz.

Hazırlayacağımız olgunluk listesi birebir Richardson Maturity Level’e bağımlı değil, ancak geliştirdiğiniz ya da kullanacağınız API’lerin ne kadar olgun olduğunu kontrol edebileceğiniz bir liste olarak düşünebilirsiniz. Bu checklist tamamen kendi deneyimlerimize göre hazırlanmış ve yukarıda anlatılan best practiceleri içermektedir. Özellikle API tasarımında tasarladığınız API’nin yeterliliğini kendiniz bu listeyi kontrol ederek ölçebilir ve bunu bir kabul kriteri olarak benimseyebilirsiniz.

  • Collection ve resource detaylarına göre doğru isimlendirme gerçekleştirilmiş mi?
  • İsimlendirme ve işleme uygun doğru HTTP metodu kullanılıyor mu?
  • Hiyerarşiler / ve çoğul ifadeler ile- betimlenmiş mi?
  • Versiyon bilgisi kurallara uygun olarak mevcut mu?
  • Doğru Http Header’ları kullanılıyor mu?
  • Response modelleri ile Dönen HTTP kodları uyuşuyor mu?
  • İlgili Http metoduyla gönderilen istek ile dönen response içeriği uygun mu?
  • Hata mesajlarında doğru HTTP kodu gönderiliyor mu? Hata detayı mevcut mu?

Rest Api tasarlarken best practice’ler göz önünde bulundurulmalı, oluşacak karmaşadan, işleri zorlaştırmaktan ve genel kullanımın dışına çıkmaktan uzak durulmalıdır. Doğru Http metotlarıyla, ideal api isimlendirme yöntemleri eşleştirilmelidir. Bunun yanında responselarımızı uygun Http Status kodları ile dönmeliyiz.

— Bana ulaşmak için: Twitter, Linkedin

— 1:1 görüşmeler için: Superpeer

https://gokhana.dev

Kaynakça

--

--

Gökhan Ayrancıoğlu
Delivery Hero Tech Hub

Software Engineer @Yemeksepeti • #Java • #Spring Boot • #Kotlin • #Spark • #Microservices • https://gokhana.dev