Metrekare ekibi olarak işin en başında hesaba kattığımız noktalardan bir tanesi tüm platformlarda (Google Glass‘ta bile) çalışacak bir ürün ortaya koymaktı. Bu yüzden yazılıma başlarken ihtiyacımız olan şey bu farklı platformlardaki uygulamaları çalıştıracak, ölçeklendirilebilir (scalable) bir servis oluşturmaktı.

REST (Representational State Transfer) mimarisini modern internet uygulamalarında popülerliği, neredeyse internet servisi standardı olması ve yazıda da belirteceğim bir kaç önemli nedenler dolasıyla RPC, SOAP gibi diğer servis mimarilerine tercih etmemizle ilgili olarak çok da düşünmeye gerek yoktu. Diğer yandan RESTful yapılar adından da anlaşılabileceği gibi diğer alternatiflerine göre uygulaması daha kolay ve anlaşılır yapıya sahip.

İlk websitemizi yaparken işi hızlandırmak için aynı kodu paylaşan ama kendi içinde neredeyse tamamen bağımsız bir dahili API yaptık. Ne de olsa, websitesi de bir client‘tır. Aynı mobil uygulamalar gibi websitesi de kendi içindeki API’la çalışıyordu. Websitesinin yayına alınmasıyla API kodlarını web client kodlarından tamamen ayırdıktan sonra RESTful standartlarına mümkün olduğu kadar uymaya çalıştık. Bu yapı sayesinde webclientı modern Backbone tabanlı hybrid web uygulaması (http://devs.metrekare.com/2014/04/27/web-uygulamalarinin-dunu-bugunu-yarini)‘yla hızlı ve kolay bir şekilde değiştirebildik. Ayrıca belirteyim, sadece Backbone değil, birçok single page application framework’leri RESTful bir API gerektiriyor.

Bir servisin RESTful olması için en başta stateless olması gerekiyor. Kısaca, state clientta tutulur ve API vermesi gereken cevabı sadece client requestine bakarak döner. Bunun en büyük avantajı scalable bir API uygulaması geliştirebilmektir. API client state agnostik olduğu için istenilen zaman ölçeklendirilebilir. Staless olmasıyla gelen daha fazla sorgu atma ihtiyacınınsa cachelenebilir cevaplar dönerek üstesinden gelinebilir.

Evet, bu mimarinin daha kolay ve esnek olmasından bahsettik ama uyulması gereken standartlar var. Bunun dışında ekip olarak yaparsak daha iyi olur diye zamanında düşündüğümüz bir kaç konu vardı:

1. URL yapısı: Bu yapıda her bir kaynak için farklı URL ve bunlar için de isim kullanılması önemli. Örnek vermek gerekirse, metrekare.com da bir ilan bilgisini almak için /get_listing/123 gibi düzensiz yerine /listings/123 olması gerekiyor. Böylece, get_listing, get_archived_listing, new_listing vb gibi yüzlerce oluşabilecek URL karmaşasından kurtulmak ve standart bir yapıya geçmek için GET, POST, PUT, PATCH, DELETE gibi HTTP fiilerini ve URL’de ise sadece isimleri kullanmak lazım.

İlan örneği dışındaki diğer tüm kaynaklar için de bu yapı geçerli.

2. Versiyonlama: Unutulmaması gereken şeylerden birisi de versiyonlama. Metrekare’de olduğu gibi özellikle bir çok client tarafından kullanılan API’ın istikrarlı olması önemli. Bu yüzden tek bir clienta dahi desteği kesmemek için API ilk yayına alındığında versiyon belirtmek gerekiyor. Versiyonu API’da Header, URL veya parametre olarak belirtmenin birkaç yolu var. Hangisinin “daha RESTful” olduğu her zaman tartışmalı olmuştur ve gerçekten uzun ve ayrı bir konu. Biz de kullandığımız Amazon Web Services’in kendi API’ın daki gibi URL’de tarih belirterek versiyonlama yaptık.

3. Genel Yapı: Client tarafında daha düzgün kod yazılmasını sağlamak amacıyla içerik ve kaynaktan bağımsız bir şekilde dönen sonucu aynı yapıda tutmak önemli. Mesela, ilan listeleme ve mesaj listeleme gibi sonuçları tamamen farklı olduğu halde tüm listeleme genel yapının

{ count: n, page: x, items: [ ... ] }
        

şeklinde olması.

Tartışmalı konulardan bir tanesi de, sonuçların ne kadar detay içereceği. Bazı REST elitistler sonucun hiyerarşik olmaması ve tek objeden oluşması gerektiğini savunuyorlar. Bence bu her ne kadar kontekste ve sisteme bağlı olsa da tek bir sorguyla bütün işi yapmaya çalışmak da doğru değil.

Diğer yandan kullandığımız özelliklerden bir tanesi de dönecek sonucun dinamik olarak belirlenmesi. Örneğin, returnPackage parametresi ile sonuçların hangi boyutta olacağına karar verebiliyoruz, böylece gereksiz data transferini ve hesaplamasını önlemekle hız performansını artırıyoruz.

4. Hata mesajları: API tasarlarken en başta düşünülecek şeylerden bir tanesi de hata mesajlarının nasıl aktarılacağı. Biz ağırlıklı olarak HTTP status codelarını kullanıyoruz. Böylece, neredeyse dokümantasyona bile gerek kalmadan sadece Response Headerları okuyarak varsa, hatanın nereden kaynaklandığını bulmak çok kolay. En sık kullandığımız kodlar: 200 (başarılı), 201 (başarıyla oluşturuldu), 204 (başarılı, bir şey dönmesine gerek yok), 400 (hatalı veri gönderilmiş), 401 (authorization yetersiz), 402 (ödeme yapmak gerekiyor),  403 (izin yok), 404 (bulunamadı), 405 (yanlış HTTP method), 409 (zaten var).

Ama tüm hataları sadece status code’ları kullanarak kapsamak imkansız. Örneğin, kullanıcı ilan eklerken hatalı bir veri göndermişse, gelen 400 statüsü sadece hatalı olduğu söyler. Ama neyin hatalı olduğunu göstermek için  kullandığımız genel bir yapı var:

[ { property_path: x, message: m }, ... ]
        

5. Format: Dönen sonuç olarak sadece json format kullanıyor olsa bile, xml de destekleyecek bir şekilde altyapı hazırladık. Bu özellikle ileride farklı ihtiyaçları olabilecek clientlar için önemli. Tarih kısımlarında tüm dillerde desteklenen ve parse edilmesi kolay olan timestamp kullandık. Web client’ta tamamen javascript kullandığımız için API’ın CORS destekli olması da önemliydi.

6. Caching: Yukarıda da kısaca belirttiğim gibi RESTful API’ların alternatiflerine göre dezavantajlardan bir tanesi clientların daha çok sorgu atma ihtiyacıdır. Bu yüzden özellikle HTTP Caching yapmak hızlandırmak ve network verimliliğini artırmak adına olmazsa olmazlardan. Header’da gerekli bilgileri girerek hangi kaynakların cachelenebilir olup olmadığı, cachelenebilirse ne kadar süreliğine olduğu vb belirtilebiliyor. Bir çok modern web frameworklerde HTTP caching mekanizması zaten hazır bir şekilde geliyor. Uygulamamızda her bir response’a sadece 2-3 satır kod ekleyerek, genel API hızını en az 2-3 katına çıkarmamız bizim quick win’lerimizdendi.

7. Authorization:  bunun için de OAuth2.0 kullanıyoruz. OAuth’a (1.0) göre entegre etmesi çok daha kolay ve en popüler autherization standardı. Güvenlik için HTTPS protokolu ile kullanılması gerekiyor. Authorization bilgisini Request Header’da belirtebilmek cachelenebilir kaynak oluşturmak adına yapılması gerekenlerden. Bu tür standartları kullanmanın güvenlik ve kolaylığı yanı sıra kullanılabilecek çok fazla sayıda kütüphane bulunması. FOSOAuthServerBundle sayesinde 1 saatte tamamen tam işlevsel authentication mekanızması kurduk.

6. Dokümantasyon: Her ne kadar bir çok geliştirici dokümantasyon yazmayı sevmese de ve zaman kaybı olarak görse de, eminim, yazmaları durumunda uzun dönemde kazançlı olucaklardır. Ayrıca otomatik dokümantasyon oluşturucular zamandan tasarruf sağlıyor. Kapsamlı yazılmış dokümantasyonlar client geliştiricileri her zaman mutlu etmiştir.

Bunlar metrekare API tasarımında karar verdiğimiz ve uyguladığımız noktalar. Değinmediğim kısımları ve ya eleştirilerinizi yorumlarda belirtebilirsiniz.