Dzisiaj chciałbym Ci poopowiadać o złotych zasadach stworzenia API. Mam nadzieję, że poniższy tekst pomoże tworzyć rozwiązania przyjazne zarówno jego użytkownikom, jak i twórcom.. Zasady, o których będę opowiadał są uniwersalne, dlatego tylko trochę zahaczymy o kwestie związane z RESTful API. Jednocześnie zachęcam do zapoznania się z specyfikacją tego standardu.
Spójność
Pierwszą rzeczą, którą musimy sobie uświadomić jest to, że nie piszemy kodu dla naszej wewnętrznej chwały. Tworzymy API by udostępnić je innym. To właśnie użytkownicy wystawią nam opinie o naszym rozwiązaniu. Dlatego najważniejsza jest konsekwencja. Najprościej wyjaśnić to na przykładzie.
Załóżmy, że nasze API udostępnia dwie metody:
- add-user
- user-delete
brakuje tu jednej spójnej wizji, konsekwencji w nazywaniu endpointów.
Lepiej nazywać metody w jednej konwencji:
- add-user
- delete-user
Ewentualnie dzieląc na konkretne obszary:
- user-add
- user-delete.
Dane wejściowe
Tutaj najważniejsza jest również – konsekwencja. Jeżeli w pierwszej stworzonej metodzie, w nazwach parametrów zastosujesz camelCase, to musisz się już trzymać tego w tej konkretnej wersji. Postaw się w roli potencjalnego użytkownika Twojego rozwiązania. Jak byś się czuł, gdyby każda metoda wymagała od Ciebie nowego podejścia? Problematyczna jest literka „s” – userId i usersid to naprawdę nie jest to samo. Ja osobiście jestem zwolennikiem stosowania „s”.
Kolejną równie ważną rzeczą na jakiej powinieneś się skupić jest bezpieczeństwo. API jest punktem wejścia do systemu. Jeżeli tu przepuścisz niebezpieczny kod, to pozostaje tylko zaufanie, że twórcy komponentów, które wykorzystujesz, zabezpieczyli się przed taką ewentualnością. W innym przypadku, właśnie pozwoliłeś komuś na wejście do Twojego systemu. Po drugie: zrób to dla siebie. Atak może Cię zaskoczyć w najmniej oczekiwanym momencie. Na początek, zdefiniuj typy parametrów, w myśl zasady „jeżeli coś ma być int-em,to ma być intem i kropka”. Dzięki temu możemy już łatwo rzutować parametry wejściowe na zdefiniowane typy (jeśli używasz typowanego języka masz łatwiej :)). Jednym z podejść jest stworzenie klasy, której jedynym celem będzie wyłuskanie parametrów żądania i ich oczyszczenie. Absolutne minimum to zabezpieczenie się przed atakami z listy OWASP Top 10.
Dane wyjściowe
Zawsze musimy być pewni co wysyłamy w odpowiedzi na zapytanie, tak by w razie błędu, nie zdradzić struktury naszej aplikacji lub schematu bazy danych. Takie niedopatrzenie wygląda w oczach odbiorców mało profesjonalnie. Nie warto też ułatwiać pracy osobom, które chciałyby zrobić coś złego z naszym systemem.
Powinieneś dać użytkownikom łatwą możliwość określenia jaki skutek ma wywołać ich żądanie. Najprostszą metodą na to jest używanie kodów odpowiedzi HTTP… Innych niż 200 i 500 :)
Dodatkowo możemy zastosować komunikaty tekstowe. Trudniej je zmapować na konkretne zachowania systemu, dlatego powinny być tylko informacją uzupełniającą. Ja w swoich rozwiązaniach nie tłumaczę komunikatów zwrotnych. Zwracam je po angielsku.
Limity
O ile nie masz nieograniczonych zasobów, zastanów się nad określaniem maksymalnej ilości obsługiwanych żądań. Zacznij od wyznaczenia jakie maksima jesteś w stanie przyjąć, np. oparciu o specyfikację serwera. Następnie ustal limity, np. 60% – 70% górnego progu – przy takim podejściu zawsze zostaje zapas mocy na nieprzewidziane sytuacje.
Czekaj, stop!
Podoba Ci się to co tworzę? Jeśli tak to zapraszam Cię do zapisania się na newsletter:Jeśli to Cię interesuje to zapraszam również na swoje social media.
Jak i do ewentualnego postawienia mi kawy :)
Kill switch
Czasami będziesz zmuszony wyłączyć daną funkcjonalność lub odciąć dostęp konkretnym osobom/aplikacjom. Powodów może być wiele, np. bezpieczeństwo (klient wysłał swoje hasło do wszystkich znajomych na FB) czy wydajność, bo aplikacja która korzysta z Twojego API właśnie się zapętla i wysyła setki żądań na sekundę, blokując całkowicie bazę danych. Jeżeli nie przygotujesz się na taką sytuację, najprawdopodobniej skończy się to dodawaniem na szybko if-a na produkcji.
A gdyby tak dodać prosty mechanizm zabezpieczający? W najprostszym przypadku może być to tabela w bazie danych, w której będziemy mieli datę początku i końca blokady oraz przyczynę. Trzy kolumny. Potem możesz przygotować proste narzędzie, które będzie dawało możliwość zarządzania blokadami.
„Panie gdzie mnie z tą logiką, toż to interfejs tylko…”
Twoje API nie powinno zawierać żadnej logiki biznesowej. Załóżmy, że musisz sprawdzić czy klient ma wykupiony dostęp do konkretnych usług. Znajdź w systemie moduł odpowiedzialny za uprawnienia i tam dopisz odpowiednie metody. W większych firmach możesz zlecić zaimplementowanie potrzebnego fragmentu odpowiedniemu zespołowi obsługującemu ten konkretny moduł.
Logi, wszędzie logi
O ile podczas developmentu możesz wszystko prześledzić w debuggerze, to na produkcji już nie jest tak wesoło. Dlatego absolutnym minimum jest logowanie kto, kiedy i jaką akcję wykonał. Kolejną rzeczą, którą powinniśmy zalogować są odpowiedzi do klienta w przypadku błędu. Dlaczego? Jeżeli klient dostanie komunikat o niepoprawnych danych wysyłanych do naszego API i je zlekceważy, będziemy mogli szybko odszukać przyczynę błędu. Tym sposobem zaoszczędzimy czas zarówno nasz, jak i naszych klientów.
Podsumowanie
Celem artykułu było przekazanie Ci kawałka swojego doświadczenia z wieloletniej pracy przy integracjach. Wiem, że część z omówionych tematów realizują gotowe biblioteki. Pytanie brzmi: czy jesteś świadom co dokładnie robią? Jeśli nie, może warto to wiedzieć? Jeśli czegoś brakuje Ci w tym co napisałem to nie krępuj się, napisz komentarz.
Autorem wpisu jest Łukasz Krawczyk, programista z kilkuletnim doświadczeniem, specjalizujący się w integracji i optymalizacji systemów informatycznych. A nade wszystko – czujny obserwator procesu dostarczania oprogramowania.
Mateusz Mazurek
Dodał bym jeszcze to, że trzeba utrzymywać aktualną dokumentację:)
Swagger pomoże :D
Fajny artykuł Mateusz. Może byś napisał o API dla newbies. Chętnie bym poczytał czym jest API oraz jak się tworzy proste API w Python.
Mam jakieś pojęcie o API ale bardzo podstawowe. Chętnie bym się dowiedział o tym więcej.
Hej.