Dacă nu sunteți foarte familiarizați cu API-urile, s-ar putea să vă întrebați …

Dacă ați fost ars de modificările API-ului, probabil că sunteți cel care ceartă. Dacă sunteți un menținător al unui API, s-ar putea să vă încercați să încercați să introduceți întrebări provocatoare precum acestea:

# Is this version 2 of just products or of the entire API?
/v2/products

# What catalyzed the change between v1 and v2? How are they different?
/v1/products
/v2/products

Aceste întrebări legate de versiuni nu sunt ușor de răspuns. Nu este întotdeauna clar ce v1 sau v2 se referă la. Și nu ar trebui să facem doar o a doua versiune a unui punct final atunci când prima nu mai este pare a ajunge.

Există motive clare De ce API-ul dvs. trebuie să aibă versiuni și există strategii clare pentru Cum pentru a naviga eficient în modificările API.

Cu toate acestea, am constatat că majoritatea dezvoltatorilor – inclusiv eu, până când am învățat câteva lecții într-un mod dificil – nu sunt conștienți de aceste motive și strategii.

Acest articol urmărește să evidențieze motivele pentru versiuni și strategiile pentru realizarea acestuia. Vom presupune un ODIHNĂ Contextul API, deoarece este un standard pentru multe API-uri, și se concentrează pe versionare aspect.

Ce este versiunea?

Ar trebui să începem cu setarea nivelului pe ceea ce se înțelege prin termenul „versiune API”. Iată definiția noastră de lucru:

Versiunea API este practica gestionării transparente a modificărilor API-ului dvs.

Versiunea este o comunicare eficientă în legătură cu modificările aduse API-ului dvs., astfel încât consumatorii să știe la ce să se aștepte. Furnizați date publicului într-un fel și trebuie să comunicați atunci când schimbați modul în care sunt livrate datele.

Ceea ce se rezumă la acest lucru, în mod esențial, este gestionarea contractelor de date și spargerea schimbărilor. Primul este elementul principal al API-ului dvs., iar cel din urmă dezvăluie de ce este necesară versiunea.

Contracte de date

Un API este o programare a aplicațiilor Interfață, iar o interfață este un impartit limita schimbului de informații. Contractul de date este inima acestei interfețe.

Un contract de date este un acord cu privire la forma și conținutul general al datelor de solicitare și / sau răspuns.

Pentru a ilustra un contract de date, iată un corp de răspuns JSON de bază:

{
  "data": [
    {
      "id": 1,
      "name": "Product 1"
    },
    {
      "id": 2,
      "name": "Product 2"
    }
  ]
}

Este un obiect cu un data proprietate care este o matrice (listă) de produse, fiecare cu un id și name proprietate. Cu exceptia data proprietatea ar fi putut fi la fel de ușor numită body, si id proprietatea fiecărui produs ar fi putut fi un GUID în loc de un număr întreg. Dacă un singur produs a fost returnat, data ar putea fi un obiect în loc de o matrice.

Aceste schimbări aparent subtile ar fi făcut pentru un alt acord, un alt contract, în ceea ce privește „forma” datelor. Forma de date s-ar putea aplica la nume de proprietăți, tipuri de date sau chiar la formatul așteptat (JSON vs. XML).

De ce este necesară versiunea?

Cu API-urile, ceva la fel de simplu ca schimbarea unui nume de proprietate din productId la productID poate sparge lucrurile pentru consumatori. Chiar acest lucru i s-a întâmplat echipei noastre săptămâna trecută.

Din fericire, am avut teste pentru a prinde modificări la contractul API. Cu toate acestea, nu ar fi trebuit să avem nevoie de aceste teste, deoarece administratorii API-ului ar fi trebuit să știe că aceasta ar fi o schimbare extremă.

Ruperea schimbărilor

Aceasta a fost o schimbare totală a contractului de date convenit, deoarece schimbarea lor ne-a obligat să ne schimbăm și aplicația.

Ce constituie o „schimbare de rupere” într-un punct final API? Orice modificare a contractului dvs. API care obligă consumatorul să facă și o modificare.

Modificările de rupere se încadrează în principal în următoarele categorii:

  1. Schimbarea formatului de solicitare / răspuns (de ex. De la XML la JSON)
  2. Schimbarea unui nume de proprietate (de ex. Din name la productName) sau tipul de date pe o proprietate (de exemplu, de la un număr întreg la un float)
  3. Adăugarea unui câmp obligatoriu la cerere (de exemplu, un nou antet obligatoriu sau o proprietate într-un corp de solicitare)
  4. Eliminarea unei proprietăți din răspuns (de exemplu, eliminarea description dintr-un produs)

Managementul modificărilor API

Nu este niciodată înțelept sau bun să forțezi consumatorii unui API să facă o schimbare. Dacă trebuie să faceți o schimbare completă, pentru asta este versiunea și vom acoperi cele mai eficiente modalități de versiune a aplicației și a punctelor finale.

Dar mai întâi să discutăm pe scurt cum să evităm să rupem schimbările în primul rând. Am putea numi această gestionare a modificărilor API.

Managementul eficient al schimbărilor în contextul unui API este rezumat prin următoarele principii:

  • Continuați asistența pentru proprietățile / punctele finale existente
  • Adăugați noi proprietăți / puncte finale, mai degrabă decât să le modificați pe cele existente
  • Proprietăți / puncte finale depășite cu atenție

Iată un exemplu care demonstrează toate aceste trei principii în contextul răspunsului pentru solicitarea datelor utilizatorului:

{
  "data": {
    "id": 1,
    "name": "Carlos Ray Norris",     // original property
    "firstName": "Carlos",           // new property
    "lastName": "Norris",            // new property
    "alias": "Chuck",                // obsolete property
    "aliases": ["Chuck", "Walker"]   // new property
  },
  "meta": {
    "fieldNotes": [
      {
        "field": "alias",
        "note": "Sunsetting on [future date]. Please use aliases."
      }
    ]
  }
}

În acest exemplu, name a fost o proprietate originală. firstName și lastName câmpurile sunt implementate pentru a oferi o opțiune mai granulară, în cazul în care consumatorul dorește să afișeze „Mr. Norris” cu unele interpolare de șiruri, dar fără a fi nevoie să analizeze name camp. Însă name proprietatea va fi susținută în mod continuu.

aliaspe de altă parte, va fi depreciat în favoarea aliases array – deoarece Chuck are atât de multe aliasuri – și există o notă în răspuns pentru a indica intervalul de timp de apus.

Cum versionați un API?

Aceste principii vor parcurge un drum lung în navigarea modificărilor API-ului dvs. fără a fi nevoie să lansați o nouă versiune. Cu toate acestea, uneori este evitat și, dacă aveți nevoie de un nou contract de date, veți avea nevoie de o nouă versiune a punctului dvs. final. Deci, va trebui să comunicați asta publicului într-un fel.

Ca o parte, rețineți că nu vorbim despre versiunea bazei de cod subiacente. Deci, dacă utilizați versiuni semantice pentru aplicația dvs. care acceptă și un API public, probabil că veți dori să separați aceste sisteme de versiune.

Cum creați o nouă versiune a API-ului dvs.? Care sunt diferitele metode pentru a face acest lucru? Va trebui să determinați ce tip a strategiei de versiune pe care doriți să o luați în general și apoi, pe măsură ce vă dezvoltați și mențineți API-ul, va trebui să determinați scop pentru fiecare modificare a versiunii.

Domeniul de aplicare

Să abordăm mai întâi domeniul de aplicare. Așa cum am explorat mai sus, uneori contractele de date vor fi compromise printr-o schimbare de rupere, ceea ce înseamnă că va trebui să furnizăm o nouă versiune a contractului de date. Aceasta ar putea însemna o nouă versiune a unui punct final sau ar putea însemna o schimbare într-un domeniu de aplicare mai global.

Ne putem gândi la niveluri de schimbare a domeniului de aplicare într-o analogie de copac:

  • Frunze – O schimbare la un punct final izolat, fără nicio relație cu alte puncte finale
  • Ramură – O schimbare la un grup de puncte finale sau la o resursă accesată prin mai multe puncte finale
  • Trompă – O modificare la nivel de aplicație, care garantează o modificare a versiunii pentru majoritatea sau toate punctele finale
  • Rădăcină – O modificare care afectează accesul la toate resursele API ale tuturor versiunilor

După cum puteți vedea, trecând de la frunză la rădăcină, schimbările devin progresiv mai impactante și cu o anvergură globală.

frunze domeniul de aplicare poate fi adesea gestionat prin gestionarea eficientă a modificărilor API. Dacă nu, pur și simplu creați un nou punct final cu noul contract de date privind resursele.

A ramură este puțin mai complicat, în funcție de câte puncte finale sunt afectate de modificarea contractului de date pentru resursa în cauză. Dacă modificările sunt relativ limitate la un grup clar de puncte finale conexe, ați putea naviga prin introducerea unui nume nou pentru resursă și actualizarea documentelor în consecință.

# variants, which has a breaking change, is accessed on multiple routes
/variants
/products/:id/variants

# we introduce product-variants instead
/product-variants
/products/:id/product-variants

A trompă se referă la modificări la nivel de aplicație care sunt adesea rezultatul unei modificări în una dintre următoarele categorii:

  • Format (de ex. Din XML la JSON)
  • Specificații (de exemplu, de la una internă la JSON API sau Deschideți API)
  • Anteturi necesare (de ex. Pentru autentificare / autorizare)

Acestea vor necesita o modificare a versiunii API generale, deci ar trebui să planificați cu atenție și să executați bine tranziția.

A rădăcină schimbarea vă va obliga să faceți un pas mai departe pentru a vă asigura că toți consumatorii tuturor versiunilor API-ului dvs. sunt conștienți de schimbare.

Tipuri de versiuni API

Pe măsură ce trecem la diferite tipuri de versiuni API, vom dori să folosim aceste informații în diferite domenii ale modificărilor API pentru a evalua tipurile. Fiecare abordare are propriul set de puncte tari și puncte slabe în abordarea modificărilor pe baza domeniului lor de aplicare.

Există mai multe metode pentru gestionarea versiunii API-ului dvs. Versiunea URI este cea mai frecventă.

Calea URI

http://www.example.com/api/v1/products
http://api.example.com/v1/products

Această strategie implică punerea numărului de versiune în calea URI-ului și se face adesea cu prefixul „v”. De cele mai multe ori, proiectanții API îl folosesc pentru a se referi la versiunea aplicației lor (adică „trunchi”) mai degrabă decât la versiunea punctului final (adică „frunză” sau „ramură”), dar aceasta nu este întotdeauna o presupunere sigură.

Versiunea URI a căilor implică versiuni orchestrate de versiuni ale aplicației care vor necesita una din cele două abordări: menținerea unei versiuni în timp ce se dezvoltă una nouă sau obligarea consumatorilor să aștepte noi resurse până la lansarea noii versiuni. Înseamnă, de asemenea, că va trebui să transferați orice punct final care nu a fost modificat de la versiune la versiune. Cu toate acestea, pentru API-urile cu volatilitate relativ scăzută, este încă o opțiune decentă.

S-ar putea să nu doriți să legați numărul versiunii dvs. cu cel al punctului final sau al resursei, deoarece ar rezulta cu ușurință în ceva de genul v4 de products dar a v1 de variants, ceea ce ar fi destul de confuz.

Interogați Params

http://www.example.com/api/products?version=1

Acest tip de versiune adaugă un parametru de interogare la cererea care indică versiunea. Foarte flexibil în ceea ce privește solicitarea versiunii resursei pe care ați dori-o la nivelul „frunzei”, dar nu deține nicio noțiune a versiunii API generale și se pretează la aceleași probleme de sincronizare menționate în comentariul de mai sus versiunea la nivel de punct final a căii URI.

Accept: version=1.0

Abordarea antetului este una care oferă mai multă granularitate în furnizarea versiunii solicitate a oricărei resurse date.

Cu toate acestea, este îngropat în obiectul cererii și nu este la fel de transparent ca opțiunea cale URI. De asemenea, este încă greu de spus dacă 1.0 se referă la versiunea punctului final sau API-ul în sine.

Integrarea tipurilor

Fiecare dintre aceste abordări pare să aibă slăbiciunea fie de a favoriza o „frunză”, fie de „trunchi”, dar nu de a le susține pe ambele.

Dacă trebuie să mențineți versiunea API generală și să oferiți, de asemenea, suport pentru mai multe versiuni de resurse, luați în considerare un amestec de tipuri URI Path și Query Params sau o abordare mai avansată a antetului.

# URI path and query params combo
http://api.example.com/v1/products?version=1
http://api.example.com/v1/products?version=2

# Extended headers, for http://api.example.com/products
Accept: api-version=1; resource-version=1
Accept: api-version=1; resource-version=2

Concluzie

Am acoperit o mulțime de terenuri aici, deci să recapitulăm:

  • Versiunea API este practica gestionării transparente a modificărilor API-ului dvs.
  • Gestionarea unui API se rezumă doar la definirea și evoluția contractelor de date și gestionarea modificărilor de rupere.
  • Cel mai eficient mod de a vă evolua API-ul fără a sparge modificările este să urmați principiile eficiente de gestionare a modificărilor API.
  • Pentru majoritatea API-urilor, versiunea în calea URI este cea mai simplă soluție.
  • Pentru API-uri mai complexe sau volatile, puteți gestiona diferite domenii de schimbare utilizând o integrare a căii URI și a interogării abordărilor params.

Deși aceste principii ar trebui să ofere o direcție clară în ceea ce privește modul de gestionare eficientă a modificărilor API-urilor dvs., evoluția unui API este mai mult o artă decât o știință. Este nevoie de gândire și previziune pentru a crea și menține un API de încredere.