Bu kılavuz bir takım HTTP+JSON API tasarımı uygulamalarını açıklar ve Heroku Platform API çalışmalarından ortaya çıkmıştır.
Bu kılavuz Heroku'nun yeni iç API'lerini yönlendirirken API hakkında da bilgi verir. Umarız Heroku dışındaki API tasarımcılarına da yarar sağlar.
Bizim amaçlarımız oyalanma tasarımından kaçarak tutarlılık ve iş mantığına odaklanmak. API tasarımı için sadece ve en ideal olan yolu değil iyi, tutarlı, güzel dokümante edilmiş bir yol aramaktayız.
Basit HTTP+JSON API arayüzü hakkında bilgi sahibi olduğunuzu varsayıyoruz ve bu kılavuz API'nin temel konularından bir çok şeyi kapsamayacak.
Bu kılavuza katkıları kabul ediyoruz.
- Temeller
- İstekler
- Uygun durum kodları(status codes) döndürün
- Uygun Olan Yerlerde Tüm Kaynakları Sunun
- İstek gövdesinde JSON Kabul Edin
- Kararlı URL Yolu Formatı Kullanın
- Kaynak İsimleri
- Olaylar
- URL ve Niteliklerde Küçük Harf Kullanın
- ID Baz Alınmadan Veri Çekmeyi Destekleyin
- Çok Dallanan URL Yollarını (nested path) Azaltın
- Cevaplar
- Artifacts
- Çeviriler
Tasarım sırasında nesneleri(kaygılarınızı) istek(request) ve cevap(response) döngüsündeki farklı bölümler arasında parçalayarak basite indirgeyin. Kuralların basit tutulması büyük ve zor sorunlar için daha fazla odaklanma sağlar.
İstek ve cevaplar belirli bir kaynak veya koleksiyon adresine yapılacaktır. Kimliği belirlemek için adres yolunu, içeriği iletmek için gövdeyi ve meta veri iletişimi için başlıkları kullanın. Sorgu parametreleri farklı durumlarda başlık(header) verilerini iletmek için kullanılabilir, ama başlıklar ile iletmek daha esnek ve daha çeşitli bilgi göndermek için tercih edilir.
API'ye erişim için istisnasız TLS(Transport Layer Security) ile güvenli bir bağlantı kullanın.
Güvensiz veri alışverişini önlemek için http veya 80 portu üzerinden gelen
isteklere yanıt vermeyerek, TLS olmayan istekleri basitçe reddet. Mümkün olmayan
ortamlarda 403 Forbidden
ile yanıt ver.
Herhangi bir net kazancı olmadan özensiz/kötü istemci(client) davranışlarına izin verdiği için yönlendirmelerden kaçınılmalıdır. İstemciler yönlendirmelere güvenerek sunucu trafiğini ikiye katlarlar ve hassas veriler ilk sorguda korunmasız kaldığından TLS kullanmanın bir anlamı olmaz.
Sürüm ve sürümler arası geçişler API'nin işletilmesi ve tasarlanması aşamasının en zor yanlarından birisidir. Bunun gibi mekanizmalar ile başlamak, en baştan bu sıkıntıları azaltmak için en iyisidir.
Süprizleri önlemek, kullanıcıların değişikliklerini kırmak için en iyi yöntem bütün isteklerde bir sürüm gerekliliği en iyisidir. Bu anlamda en iyisi, daha sonra bir değişiklik yapmanın zor olmasını engellemek için varsayılan versiyonlar kullanılmamalıdır.
Sürüm özelliklerini başlıklar içerisinde diğer meta veriler ile birlikte sunmak
en iyisidir. Accept
başlığını kullanma örneği:
Accept: application/vnd.heroku+json; version=3
Bütün cevaplar(responses), dönen cevabın içeriğine özel tanımlayıcılar olan,
ETag
içermelidir. Bu kullanıcılara kaynakları önbelleklemek için ve kendi
belleğindeki(cache) verinin güncellenip güncellemeyeceğini karşılaştırmak
için isteklerde kullanmalarına izin verir.
If-None-Match
Her API cevabında UUID değeri ile doldurulmuş, Request-Id
başlığı olmalıdır.
Bu değeri, istekleri trace
, diagnose
ve debug
edebilmek için, sunucu,
istemci(client) ve diğer her servis tarafında günlükleme (logging) için
kullanabilirsiniz.
Yüksek boyutlardaki cevapları(responses) Range
başlığını kullanarak bir kaç parçaya bölebilirsiniz. İstek ve cevapların
başlıklar, durum kodları, limitler, sıralama ve tekrarlama(iteration) hakkında
daha fazla bilgi için Heroku Platform API discussion of Ranges
bağlantısını takip edebilirsiniz.
Her cevapla birlikte cevaba uygun HTTP durum kodları döndürün. Başarılı cevaplar bu kılavuza uygun olmalı:
200
:GET
metodu ile yapılan istek başarılıdır.DELETE
veyaPATCH
metodu ile yapılan istekler(requests) senkronlu bir şekilde tamamlanmıştır, veyaPUT
metodu ile yapılan istek(request) senkron bir şekilde var olan içeriği güncellemiştir.201
: Senkron bir şekilde yapılanPOST
veya senkron bir şekilde yeni bir içerik yaratanPUT
isteği(request) başarılı olarak tamamlanmıştır.202
:POST
,PUT
,DELETE
, veyaPATCH
metodu ile asenkron bir şekilde yapılan istek(request) kabul edilmiştir.206
:GET
ile yapılan istek(request) başarılıdır, fakat sadece cevabın(response) bir kısmı dönmüştür: bakınız Range'ler ile Yüksek Boyuttaki Cevapları Parçalara Bölün
Doğrulama(authentication) ve yetki(authorization) kodlarını kullanmaya dikkat edin:
401 Unauthorized
: Kullanıcı yetkilendirilmediği için istek(request) başarısız olmuştur.403 Forbidden
: Kullanıcının belli bir bölüme ulaşma yetkisi olmadığı için istek başarısız olmuştur.
Ek bilgilerin eklenmesi konusunda bir hata yapıldığında uygun kodları dönün:
422 Unprocessable Entity
: Yapılan istek anlamlandırılmış, fakat gönderilen parametler doğru değildir.429 Too Many Requests
: Yapılan istek sayısı izin verilen limite ulaşmıştır, tekrar deneyin.500 Internal Server Error
: Sunucu üzerinde bir şeyler ters gitti, kontrol edin veya yetkili kişiye rapor edin.
Sunucu ve kullanıcı hata durum kodları hakkında daha fazla bilgi için HTTP response code spec adresini kontrol edebilirsiniz.
İmkan olduğu sürece tüm kaynakları
(örneğin: bir nesnenin tüm niteliklerini) cevap olarak dönün. PUT
/PATCH
ve DELETE
istekleri dahil, 200
ve 201
cevaplarında daima tüm kaynakları geri döndürün, örneğin:
$ curl -X DELETE \
https://service.com/apps/1f9b/domains/0fd4
HTTP/1.1 200 OK
Content-Type: application/json;charset=utf-8
...
{
"created_at": "2012-01-01T12:00:00Z",
"hostname": "subdomain.example.com",
"id": "01234567-89ab-cdef-0123-456789abcdef",
"updated_at": "2012-01-01T12:00:00Z"
}
202 cevapları
kaynakları(resources) içermeyecektir.
Örneğin:
$ curl -X DELETE \
https://service.com/apps/1f9b/dynos/05bd
HTTP/1.1 202 Accepted
Content-Type: application/json;charset=utf-8
...
{}
form-encoded
veriyi kullanmak ya da buna ek olarak kullanmak yerine
PUT
/PATCH
/POST
istek gövdelerinde JSON verilerini kabul edin. Bu bir
JSON-serialized cevap gövdesi ile bir simetri oluşturacaktır.
$ curl -X POST https://service.com/apps \
-H "Content-Type: application/json" \
-d '{"name": "demoapp"}'
{
"id": "01234567-89ab-cdef-0123-456789abcdef",
"name": "demoapp",
"owner": {
"email": "[email protected]",
"id": "01234567-89ab-cdef-0123-456789abcdef"
},
...
}
Eğer kaynak sistemde tekil değilse kaynakların çoğul isimlerini kullanın (Örneğin, bir çok sistemde kullanıcıların sadece bir kullanıcı hesabı vardır). Bu özel kaynaklara tutarlılık katar.
Özel bir aksiyon gerektiren durumlarda olayları standart bir actions
öneki ile
tanımlayın:
/resources/:resource/actions/:action
Örneğin:
/runs/{run_id}/actions/stop
URL yolu için küçük harf ve -
ile ayrılmış (dash-seperated) isimler kullanın.
Örneğin:
service-api.com/users
service-api.com/app-setups
Nitelikleri(attributes) küçük harfe çevirin, kelimeleri _
kullanarak ayırın.
Böylelikle Javascript'te tırnak işareti kullanmadan yazılabilir. Örneğin:
service_class: "first"
Bazı durumlarda son kullanıcı için kaynağı ID ile ilişkilendirmek kolay olmayabilir. Örneğin, kullanıcılar Heroku uygulama isimleri üzerinden düşünüyorlar, ama uygulama bir UUID tarafından ile ilişkilendirilmiş olabilir. Bu durumda id ve name terimlerinin ikisi ile de ulaşılabilir olabilir. Örneğin:
$ curl https://service.com/apps/{app_id_or_name}
$ curl https://service.com/apps/97addcf0-c182
$ curl https://service.com/apps/www-prod
Bu ID'yi hariç tutup sadece isim ile veri çekilebilir yapmak değildir.
Veri modelinizdeki iç içe ilişkili kaynaklar için çok fazla alt dalları oluşan URL yollarından kaçının. Aşağıdaki örnek bu karmaşık yapıya örnektir.
/orgs/{org_id}/apps/{app_id}/dynos/{dyno_id}
Derin ilişkilerin dallanan yollarını ana yollarda kalacak şekilde kısıtlayın.
Örneğin, aşağıdaki örnekte dyno
bir organizasyonun uygulamasıdır:
/orgs/{org_id}
/orgs/{org_id}/apps
/apps/{app_id}
/apps/{app_id}/dynos
/dynos/{dyno_id}
Her kaynak için varsayılan olarak bir id
parametresi oluşturun. Kullanmamak
için iyi bir sebebiniz olana kadar UUID'leri kullanın. Tüm heryerde tekil
olmayan ID'leri kullanmayın. ID'ler genelde otomatik artan (autoincrement)
olduğu için diğer servislerde yine karşılaşılabilirdir.
UUID'lerin formatı küçük harflerle ve 8-4-4-4-12
şeklindedir. Örneğin:
"id": "01234567-89ab-cdef-0123-456789abcdef"
created_at
ve updated_at
zaman damgalarını varsayılan olarak oluşturun.
Örneğin:
{
// ...
"created_at": "2012-01-01T12:00:00Z",
"updated_at": "2012-01-01T13:00:00Z",
// ...
}
Bu zaman damgaları bazı kaynaklar için mantıklı olmayabilir, bu durumlarda kullanmayabilirsiniz.
ISO8601 formatında, UTC zamanını kullanın. Örneğin:
"finished_at": "2012-01-01T12:00:00Z"
Foreign Key
referanslarını dallanan nesneler(nested object) ile gösterin,
Örneğin:
{
"name": "service-production",
"owner": {
"id": "5d8201b0..."
},
// ...
}
Aşağıdaki gibi yapmamaya çalışın:
{
"name": "service-production",
"owner_id": "5d8201b0...",
// ...
}
Bu yaklaşım gerektiğinde size bu dallanan nesneler hakında, cevabınızın yapısını değiştirmeden ya da bozmadan daha fazla bilgi verebilme imkanı sağlar. Örneğin:
{
"name": "service-production",
"owner": {
"id": "5d8201b0...",
"name": "Alice",
"email": "[email protected]"
},
// ...
}
Tutarlı, belirli yapıda hatalar oluşturun. Bir programın okuyabileceği hata
id
'leri ve insanların anlayabileceği hata message
'ları, daha fazla bilgi
alabilecekleri ve çözüm bulabilecekleri hata url
'leri belirleyin. Örneğin:
HTTP/1.1 429 Too Many Requests
{
"id": "rate_limit",
"message": "Account reached its API rate limit.",
"url": "https://docs.service.com/rate-limits"
}
Kullanıcılarınız karşılaşabileceği hata formatlarınızı dokümante edin.
Rate Limit
diğer kullanıcıların servisi sağlıklı kullanabilmeleri için
istemcinin isteklerinini sınırlandırılmasıdır. Bunun için
token bucket algorithm
algoritmasını kullanabilirsiniz.
Kalan istek sayılarını RateLimit-Remaining
başlığı içerisinde sunun.
Ekstra boşluklar isteklerin cevaplarını gereksiz yere büyütür, ve bir çok istemci zaten otomatik olarak bu verileri güzel görünür hale getirmektedir. En iyisi JSON verinizi sıkıştırılmış halde tutmaktır. Örneğin:
{"beta":false,"email":"[email protected]","id":"01234567-89ab-cdef-0123-456789abcdef","last_login":"2012-01-01T12:00:00Z","created_at":"2012-01-01T12:00:00Z","updated_at":"2012-01-01T12:00:00Z"}
Aşağıdakinin yerine:
{
"beta": false,
"email": "[email protected]",
"id": "01234567-89ab-cdef-0123-456789abcdef",
"last_login": "2012-01-01T12:00:00Z",
"created_at": "2012-01-01T12:00:00Z",
"updated_at": "2012-01-01T12:00:00Z"
}
Sorgu paramatreleri ile (Örneğin: ?pretty=true
) veya başlık parametresi
(Örneğin: Accept: application/vnd.heroku+json; version=3; indent=4;
) olarak
kullanıcılarınıza güzel görünümlü hali için bir seçenek sunabilirsiniz.
API arayüzünüzü ayrıntılarıyla belirtmek için programların anlayabileceği
şemalar oluşturun. Şemanızı yönetmek için prmd
kullanabilirsiniz, ve prmd verify
ile geçerliliğini doğrulayın.
API'nizi kullanmak isteyecek istemci developerların anlayabileceği dokümanlar oluşturun.
Eğer daha önce bahsedildiği gibi prmd ile bir şema oluşturursanız, prmd doc
komutu ile son kullanıcılarınıza kolayca Markdown dokümanı oluşturabilirsiniz
Son kullanıcılara ek olarak, aşağıdaki konuları içeren bir API'ye genel bakış bölümün oluşturun:
- Authentication(Kimlik DOğrulama), bilgilerini edinme ve kullanımı.
- API kararlılığı ve versiyonlama, API versiyonlarını seçimi
- Genel istek ve cevap başlıkları
- Hata formatları
- API arayüzünü farklı dillerdeki kullanıcılar ile kullanım örnekleri
Kullanıcılarınızın terminal arayüzünden direk çağırabileceği çalıştırılabilir örnekler hazırlayın. Bu örneklerin, kullanıcının API arayüzü ile çalışırken minimum enerjiyi harcaması için, mümkün olduğunca kelimesi kelimesine kullanılabilir olmasını sağlayın. Örneğin:
$ export TOKEN=... # acquire from dashboard
$ curl -is https://$TOKEN@service.com/users
Eğer siz Markdown dokümanı oluştururken prmd kullanırsanız, ücretsiz, her son kullanıcı örnekleriniz olur.
API'nizin istikrarlılığını açıklayın veya kararlılığına ve olgunluğuna göre birden fazla bağlantı seçeneği sunun, örneğin: prototype/development/production seçenekleri ile.
Heroku API compatibility policy dokümanını, yönetim yaklaşımlarınının değişikliği ve olası kararlılıkları için, örnek alabilirsiniz.
API arayüzünüz kararlı ve production'a bir kez çıktıktan sonra geriye dönük versiyon uyumsuzlukları çıkarmamaya çalışın. Eğer böyle bir uyumsuzluk olması gerekiyor ise yeni bir version numarası ile API çıkarın.
- Korean version (based on f38dba6), by @yoondo
- Simplified Chinese version (based on 337c4a0), by @ZhangBohan
- Traditional Chinese version (based on 232f8dc), by @kcyeu