Прагматичен API дизајн: Градење за промени, не само за денес
API е ветување дадено во код. Штом друга услуга, мобилна апликација, партнер или внатрешен тим ќе се потпре на него, менувањето на тоа ветување станува многу поскапо од додавањето уште една крајна точка. Тешкиот дел не е денес да се произведе JSON. Туку да се дизајнираат граници што му овозможуваат на системот да еволуира без секој потрошувач да мора да се движи во ист ритам.
Прагматичниот дизајн на API започнува со едноставен начин на размислување: оптимизирајте за разбирлива промена. Тоа значи избор на конвенции што се доволно едноставни за да бидат предвидливи, доволно експлицитни за да бидат безбедни и доволно флексибилни за да ги прифатат барањата со кои сѐ уште не сте се сретнале.
Дизајнирајте околу можности, а не околу табели во базата на податоци
Честа рана грешка е директното изложување на моделот за перзистенција. Ако базата на податоци има табели orders, order_items и customers, може да изгледа природно да се создадат соодветни CRUD крајни точки. Тој пристап е брз, но прави API-то да ја наследи секоја одлука за складирање.
Потрошувачите обично не се грижат како се нормализирани податоците. Тие се грижат за можностите: правење нарачка, прегледување на нејзиниот статус на испорака, откажување под валидни услови или преземање фактура. Тоа се корисни API концепти бидејќи можат да останат стабилни дури и кога се менуваат шемата, моделот на редици или границите на внатрешните услуги.
На пример, претставувањето на нарачка може да ги содржи полињата што му се потребни на клиентот без да ја пресликува секоја колона:
{
"id": "ord_123",
"status": "processing",
"total": {
"amount": "49.90",
"currency": "EUR"
},
"customer": {
"id": "cus_456",
"name": "Avery Chen"
}
}
Внатрешната имплементација подоцна може да ги подели податоците за клиентот во друга услуга или поинаку да ги пресметува вкупните износи. Ако јавниот договор остане намерно осмислен, потрошувачите не би требало да мораат да знаат.
Направете ги договорите експлицитни и предвидливи
Доследноста е една од највредните карактеристики што може да ги понуди едно API. Изберете конвенции за именување, датуми, идентификатори, страницирање, одговори за грешки и nullable полиња, а потоа применувајте ги насекаде. На потрошувачот не би требало да му треба ново толкување за секој ресурс.
Одлучете рано дали JSON својствата ќе користат snake_case или camelCase. Користете еден формат за датуми, по можност недвосмислена ISO 8601 претстава кога времето е релевантно. Претставувајте ги парите намерно: вредностите со подвижна запирка повикуваат суптилни грешки, па износ како децимална низа или цел број во помали единици често е побезбеден кога е доследно документиран.
Одговорите за грешки заслужуваат иста грижа како и успешните. Генеричка грешка на серверот понекогаш е неизбежна, но грешките при валидација и во доменот треба да овозможуваат дејствување.
{
"error": {
"code": "invalid_state_transition",
"message": "An order can only be cancelled before shipment.",
"details": {
"current_status": "shipped"
}
}
}
HTTP статусот ја соопштува широката класа на неуспех. Стабилниот код за грешка им овозможува на клиентите да донесат одмерена одлука. Пораката му помага на развивачот да го дијагностицира проблемот. Избегнувајте клиентите да анализираат проза и не изложувајте stack traces, SQL пораки или детали за внатрешната инфраструктура.
Користете HTTP семантика без да станете догматични
REST конвенциите се корисни бидејќи го намалуваат изненадувањето. GET не треба да менува состојба. POST најчесто создава ресурс или иницира процес. PATCH е добро прилагоден за делумни ажурирања. Статусните кодови треба да го одразуваат исходот, наместо само да потврдуваат дека кодот на апликацијата бил извршен.
Но прагматизмот е поважен од вклопувањето на секоја деловна акција во URL во облик на именка. Некои операции се акции со правила, несакани ефекти и асинхрона работа. Крајна точка како POST /orders/ord_123/cancel може да биде појасна од двосмислено делумно ажурирање кога откажувањето активира враќање на залихите, обработка на плаќањето и известувања.
Важно прашање е дали крајната точка го прави однесувањето на доменот очигледно. Уреден URL не е замена за доверлив договор.
Планирајте за повторни обиди пред продукцијата да ви ја одржи лекцијата
Мрежите откажуваат на незгодни начини. Клиент може да испрати барање, да го изгуби одговорот и повторно да се обиде иако серверот ја завршил првичната операција. Ова е особено опасно за операции што создаваат плаќања, нарачки, покани или надворешни несакани ефекти.
За операции за создавање што можат повторно да се обидат, поддржете клуч за идемпотентност. Клиентот генерира единствен клуч и го испраќа со барањето; серверот го складира клучот со резултирачката операција и го враќа истиот исход за соодветен повторен обид. Во PHP, имплементацијата треба проверката на клучот и трајното создавање на деловниот запис да ги направи дел од една внимателно дизајнирана граница на трансакција.
Не третирајте ја идемпотентноста како заглавие што можете да го додадете подоцна без дизајнерска работа. Дефинирајте што ги прави две барања еквивалентни, колку долго се задржуваат клучевите и што се случува ако истиот клуч повторно се употреби со различен товар на барањето. Враќањето јасен одговор за конфликт е побезбедно од тивко применување неочекувана операција.
Верзионирајте ретко и еволуирајте адитивно
Верзионирањето не е дозвола лежерно да испорачувате промени што ја нарушуваат компатибилноста. Нова главна верзија создава оперативна работа: документацијата се раздвојува, клиентите мигрираат со различни брзини, матриците за тестирање растат, а старото однесување има потреба од план за повлекување.
Кога е можно, претпочитајте компатибилни дополнувања. Додавањето опционално поле во одговорот често е безбедно. Додавањето нов опционален параметар за пребарување обично е безбедно. Отстранувањето поле, менувањето на неговиот тип, редефинирањето вредност или менувањето на однесувањето на страницирањето не е.
Кога е неопходна промена што ја нарушува компатибилноста, направете ја видлива и ограничена. Патека како /v2/orders лесно се открива и рутира, додека верзионирањето преку заглавија може да ги одржи URL-адресите почисти, но бара посилни алатки и документација. И двата избора можат да функционираат. Важно е да имате јасна политика за компатибилност, насоки за миграција и наведен процес за застарување.
Страницирањето, филтрирањето и перформансите се прашања на договорот
Крајна точка што работи со десет редови може да стане продукциски инцидент со десет милиони. API-јата за колекции имаат потреба од ограничувања уште од почеток. Користете документирана максимална големина на страница, детерминистичко подредување и формат на одговор што им кажува на потрошувачите како да продолжат.
Страницирањето со курсор често е силен избор за големи колекции или колекции што често се менуваат, бидејќи ги избегнува нестабилноста и растечкиот трошок што може да ги воведе страницирањето засновано на поместување. Исто така бара внимателно подредување и индексирање. Ако крајна точка подредува по created_at и користи идентификатор за разрешување на еднаквите вредности, базата на податоци треба да има индекс што ја поддржува таа шема на пристап.
И филтрирањето има потреба од ограничувања. Изложувањето произволни филтри за полиња или изрази за пребарување слични на бази на податоци ги прави авторизацијата, валидацијата и перформансите многу потешки за контролирање. Понудете филтри што одговараат на вистинските потреби на корисниците, валидирајте ги нивните вредности и документирајте ја нивната интеракција со подредувањето и страницирањето.
Одржувајте ја PHP границата тенка
Во PHP backend, контролерите треба да го преведуваат HTTP во повици кон апликацијата, а не да станат место каде што се акумулираат доменските правила. Валидирајте ја формата на барањето на работ, авторизирајте го актерот, повикајте апликациска услуга или обработувач на команди и мапирајте го резултатот во одговор.
Ова раздвојување се исплатува кога истото однесување подоцна треба да се изврши од worker за редици, задача од командната линија или друго API. Исто така ги прави тестовите покорисни: доменските правила може да се тестираат без да се гради HTTP барање за секој случај, додека тестовите на крајните точки се фокусираат на рутирање, серијализација, автентикација и статусни кодови.
Исто така, одолејте на тоа ORM ентитетите да станат ваши јавни објекти за одговор. Посветените модели за барања и одговори создаваат мала количина работа за мапирање, но спречуваат случајно изложување полиња и ја раздвојуваат еволуцијата на API-то од промените во перзистенцијата.
Документирајте го однесувањето, па потоа проверете го документот
Документацијата е дел од производот, а не дополнителна мисла. Опишете ги автентикацијата, потребните дозволи, полињата на барањето, формите на одговорите, кодовите за грешки, правилата за страницирање и однесувањето при повторен обид. Примерите помагаат, но само примерите не се договор.
Форматот на шема може да обезбеди корисна структура, особено кога го води генерирањето клиенти или договорните тестови. Држете го блиску до имплементацијата и осигурете се автоматизираните тестови да ги проверуваат важните претпоставки. Документирано поле што серверот никогаш не го испраќа е погрешно; недокументирано поле што клиентите почнуваат да го користат станува случајна обврска.
Изградете API-ја што оставаат простор за размислување
Најдоброто API ретко е она со најмногу апстракција или со најмалку крајни точки. Тоа е она што им помага на потрошувачите да завршат вистинска работа, истовремено зачувувајќи ја способноста на тимот одговорно да го менува системот.
Направете ја вообичаената патека јасна. Направете ги неуспесите разбирливи. Направете ги повторните обиди безбедни. Чувајте ги деталите за складирањето приватни и третирајте го секој одговор како ветување со трошок за одржување. Кога ќе дојде промената — а таа секогаш доаѓа — прагматичното API ја претвора од координирана вонредна состојба во вообичаена инженерска работа.