Πώς να εκδώσετε ένα REST API

Εάν δεν είστε πολύ εξοικειωμένοι με τα API, ίσως αναρωτιέστε ... γιατί όλη η φασαρία για την έκδοση API;

Εάν σας έχουν καεί από αλλαγές API, πιθανότατα είστε αυτός που αναστατώνει. Εάν είστε συντηρητής ενός API, μπορεί επίσης να ανησυχείτε για την προσπάθεια υποβολής προκλητικών ερωτήσεων όπως αυτές:

# 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

Αυτές οι ερωτήσεις σχετικά με την έκδοση δεν είναι εύκολο να απαντηθούν. Δεν είναι πάντα σαφές ως προς το τι αναφέρεται v1ή v2αναφέρεται. Και δεν πρέπει απλώς να φτιάξουμε μια δεύτερη έκδοση ενός τελικού σημείου, όταν η πρώτη δεν φαίνεται πλέον να αρκεί.

Υπάρχουν σαφείς λόγοι για τους οποίους το API σας πρέπει να διαθέτει εκδόσεις και υπάρχουν σαφείς στρατηγικές για τον τρόπο αποτελεσματικής πλοήγησης των αλλαγών API.

Ωστόσο, διαπίστωσα ότι οι περισσότεροι προγραμματιστές - συμπεριλαμβανομένου του εαυτού μου, έως ότου έμαθα μερικά μαθήματα με τον σκληρό τρόπο - δεν γνωρίζουν αυτούς τους λόγους και τις στρατηγικές.

Αυτό το άρθρο επιδιώκει να επισημάνει αυτούς τους λόγους για την έκδοση και τις στρατηγικές για την επίτευξή του. Θα αναλάβουμε ένα REST API περιβάλλον, καθώς είναι πρότυπο για πολλά API και θα επικεντρωθούμε στην έκδοση .

Τι είναι το Versioning;

Θα πρέπει να ξεκινήσουμε με τη ρύθμιση του επιπέδου σχετικά με το τι σημαίνει ο όρος "έκδοση API". Εδώ είναι ο ορισμός εργασίας μας:

Η έκδοση API είναι η πρακτική της διαφανούς διαχείρισης αλλαγών στο API σας.

Η έκδοση είναι αποτελεσματική επικοινωνία σχετικά με τις αλλαγές στο API σας, ώστε οι καταναλωτές να γνωρίζουν τι να περιμένουν από αυτό. Παρέχετε δεδομένα στο κοινό με κάποιο τρόπο και πρέπει να επικοινωνείτε όταν αλλάζετε τον τρόπο παράδοσης των δεδομένων.

Αυτό που καταλήγει σε αυτό, στο nitty gritty, είναι η διαχείριση συμβάσεων δεδομένων και η διακοπή αλλαγών. Το πρώτο είναι το κύριο δομικό στοιχείο του API σας και το δεύτερο αποκαλύπτει γιατί απαιτείται έκδοση.

Συμβάσεις δεδομένων

Ένα API είναι μια διεπαφή προγραμματισμού εφαρμογών και μια διεπαφή είναι ένα κοινό όριο για την ανταλλαγή πληροφοριών. Η σύμβαση δεδομένων είναι η καρδιά αυτής της διεπαφής.

Η σύμβαση δεδομένων είναι μια συμφωνία σχετικά με το σχήμα και το γενικό περιεχόμενο του αιτήματος ή / και των δεδομένων απόκρισης.

Για να απεικονίσετε μια σύμβαση δεδομένων, εδώ είναι ένα βασικό σώμα απόκρισης JSON:

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

Είναι ένα αντικείμενο με μια dataιδιότητα που είναι ένας πίνακας (λίστα) προϊόντων, το καθένα με ένα idκαι nameιδιότητα. Αλλά η dataιδιότητα θα μπορούσε να κληθεί εξίσου εύκολα bodyκαι η idιδιότητα σε κάθε προϊόν θα μπορούσε να ήταν ένα GUID αντί για ακέραιο. Εάν ένα προϊόν επιστρέφονταν, dataθα μπορούσε να είναι ένα αντικείμενο αντί ενός πίνακα.

Αυτές οι φαινομενικά λεπτές αλλαγές θα είχαν γίνει για μια διαφορετική συμφωνία, μια διαφορετική σύμβαση, σχετικά με το "σχήμα" των δεδομένων. Το σχήμα δεδομένων μπορεί να ισχύει για ονόματα ιδιοτήτων, τύπους δεδομένων ή ακόμη και την αναμενόμενη μορφή (JSON έναντι XML).

Γιατί απαιτείται έκδοση;

Με τα API, κάτι τόσο απλό όσο η αλλαγή ενός ονόματος ιδιοκτησίας productIdσε productIDμπορεί να καταστρέψει τα πράγματα για τους καταναλωτές. Αυτό συνέβη στην ομάδα μας την περασμένη εβδομάδα.

Ευτυχώς, είχαμε δοκιμές για να εντοπίσουμε αλλαγές στη σύμβαση API. Ωστόσο, δεν θα έπρεπε να χρειαζόμαστε αυτές τις δοκιμές, επειδή οι συντηρητές του API θα έπρεπε να γνωρίζουν ότι αυτό θα ήταν μια σπάσιμο αλλαγή.

Σπάζοντας αλλαγές

Αυτή ήταν μια σπάνια αλλαγή στο συμφωνηθέν συμβόλαιο δεδομένων επειδή η αλλαγή τους μας ανάγκασε να αλλάξουμε και την αίτησή μας.

Τι αποτελεί "αλλαγή αλλαγής" σε ένα τελικό σημείο API; Οποιαδήποτε αλλαγή στη σύμβαση API που αναγκάζει τον καταναλωτή να κάνει επίσης μια αλλαγή.

Οι σπάνιες αλλαγές ταιριάζουν κυρίως στις ακόλουθες κατηγορίες:

  1. Αλλαγή της μορφής αίτησης / απόκρισης (π.χ. από XML σε JSON)
  2. Αλλαγή ονόματος ιδιοκτησίας (π.χ. από nameσε productName) ή τύπου δεδομένων σε μια ιδιότητα (π.χ. από ακέραιο σε float)
  3. Προσθήκη ενός απαιτούμενου πεδίου στο αίτημα (π.χ. μια νέα απαιτούμενη κεφαλίδα ή ιδιότητα σε ένα σώμα αιτήματος)
  4. Κατάργηση μιας ιδιότητας στην απόκριση (π.χ. κατάργηση descriptionαπό ένα προϊόν)

Διαχείριση αλλαγών API

Δεν είναι ποτέ σοφό ή ευγενικό να αναγκάζουμε τους καταναλωτές ενός API να κάνουν μια αλλαγή. Αν πρέπει να κάνετε μια αλλαγή, αυτό είναι το περιεχόμενο της έκδοσης και θα καλύψουμε τους πιο αποτελεσματικούς τρόπους για την έκδοση της εφαρμογής και των τελικών σημείων σας.

Αλλά πρώτα ας συζητήσουμε εν συντομία πώς να αποφευχθούν οι αλλαγές στην πρώτη. Θα μπορούσαμε να καλέσουμε αυτήν τη διαχείριση αλλαγών API.

Η αποτελεσματική διαχείριση αλλαγών στο πλαίσιο ενός API συνοψίζεται από τις ακόλουθες αρχές:

  • Συνεχίστε την υποστήριξη για υπάρχουσες ιδιότητες / τελικά σημεία
  • Προσθέστε νέες ιδιότητες / τελικά σημεία αντί να αλλάξετε τα υπάρχοντα
  • Στοχαστικό ηλιοβασίλεμα ξεπερασμένων ιδιοτήτων / τελικών σημείων

Ακολουθεί ένα παράδειγμα που δείχνει και τις τρεις αυτές αρχές στο πλαίσιο της απόκρισης για αίτημα δεδομένων χρήστη:

{ "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." } ] } }

Σε αυτό το παράδειγμα, nameήταν μια αρχική ιδιότητα. Τα πεδία firstNameκαι τα lastNameπεδία εφαρμόζονται για να παρέχουν μια πιο λεπτομερή επιλογή, σε περίπτωση που ο καταναλωτής θέλει να εμφανίσει τον "κ. Norris" με κάποια παρεμβολή συμβολοσειράς αλλά χωρίς να χρειάζεται να αναλύσει το nameπεδίο. Ωστόσο, η nameιδιοκτησία θα υποστηρίζεται συνεχώς.

alias, από την άλλη πλευρά, πρόκειται να καταργηθεί υπέρ της aliasesσυστοιχίας - επειδή ο Τσακ έχει τόσα πολλά ψευδώνυμα - και υπάρχει μια σημείωση στην απάντηση που υποδεικνύει το χρονικό πλαίσιο λήξης.

Πώς εκδίδετε ένα API;

Αυτές οι αρχές θα συμβάλουν πολύ στην πλοήγηση των αλλαγών στο API σας χωρίς να χρειάζεται να κυκλοφορήσετε μια νέα έκδοση. Ωστόσο, μερικές φορές μπορεί να αποφευχθεί και εάν χρειάζεστε ένα ολοκαίνουργιο συμβόλαιο δεδομένων, θα χρειαστείτε μια νέα έκδοση του τελικού σημείου σας. Επομένως, θα πρέπει να το κοινοποιήσετε στο κοινό με κάποιο τρόπο.

Εκτός αυτού, σημειώστε ότι δεν μιλάμε για την έκδοση της υποκείμενης βάσης κώδικα. Επομένως, εάν χρησιμοποιείτε σημασιολογική έκδοση για την εφαρμογή σας που υποστηρίζει επίσης ένα δημόσιο API, πιθανότατα θα θέλετε να διαχωρίσετε αυτά τα συστήματα εκδόσεων.

Πώς δημιουργείτε μια νέα έκδοση του API σας; Ποιες είναι οι διαφορετικές μέθοδοι για αυτό; Θα πρέπει να καθορίσετε τον τύπο στρατηγικής εκδόσεων που θέλετε να ακολουθήσετε γενικά και, στη συνέχεια, καθώς αναπτύσσετε και διατηρείτε το API σας, θα πρέπει να καθορίσετε το εύρος κάθε αλλαγής έκδοσης.

Πεδίο εφαρμογής

Ας αντιμετωπίσουμε πρώτα το πεδίο εφαρμογής. Όπως διερευνήσαμε παραπάνω, μερικές φορές τα συμβόλαια δεδομένων θα διακυβευθούν από μια αλλαγή, και αυτό σημαίνει ότι θα πρέπει να παρέχουμε μια νέα έκδοση του συμβολαίου δεδομένων. Αυτό θα μπορούσε να σημαίνει μια νέα έκδοση ενός τελικού σημείου ή θα μπορούσε να σημαίνει μια αλλαγή σε ένα πιο παγκόσμιο πεδίο εφαρμογής.

Μπορούμε να σκεφτούμε τα επίπεδα αλλαγής του εύρους σε μια αναλογία δέντρων:

  • Leaf - Μια αλλαγή σε ένα απομονωμένο τελικό σημείο χωρίς σχέση με άλλα τελικά σημεία
  • Υποκατάστημα - Μια αλλαγή σε μια ομάδα τελικών σημείων ή σε έναν πόρο στον οποίο έχετε πρόσβαση μέσω διαφόρων σημείων
  • Trunk - Μια αλλαγή σε επίπεδο εφαρμογής, που δικαιολογεί αλλαγή έκδοσης στα περισσότερα ή σε όλα τα τελικά σημεία
  • Root - Μια αλλαγή που επηρεάζει την πρόσβαση σε όλους τους πόρους API όλων των εκδόσεων

Όπως μπορείτε να δείτε, μεταβαίνοντας από φύλλο σε ρίζα, οι αλλαγές γίνονται προοδευτικά πιο σημαντικές και παγκόσμιες.

Το εύρος των φύλλων μπορεί συχνά να αντιμετωπιστεί μέσω αποτελεσματικής διαχείρισης αλλαγών API. Εάν όχι, απλώς δημιουργήστε ένα νέο τελικό σημείο με τη νέα σύμβαση δεδομένων πόρων.

Ένας κλάδος είναι λίγο πιο δύσκολος, ανάλογα με το πόσα τελικά σημεία επηρεάζονται από την αλλαγή της σύμβασης δεδομένων στον εν λόγω πόρο. Εάν οι αλλαγές περιορίζονται σε μια σαφή ομάδα σχετικών τελικών σημείων, θα μπορούσατε ενδεχομένως να το πλοηγηθείτε εισάγοντας ένα νέο όνομα για τον πόρο και ενημερώνοντας τα έγγραφά σας ανάλογα.

# 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

Ένας κορμός αναφέρεται σε αλλαγές σε επίπεδο εφαρμογής που είναι συχνά αποτέλεσμα μιας αλλαγής σε μία από τις ακόλουθες κατηγορίες:

  • Μορφή (π.χ. από XML έως JSON)
  • Προδιαγραφή (π.χ. από ένα εσωτερικό έως JSON API ή Open API)
  • Απαιτούμενες κεφαλίδες (π.χ. για έλεγχο ταυτότητας / εξουσιοδότηση)

Αυτά θα απαιτήσουν μια αλλαγή στη συνολική έκδοση του API σας, επομένως θα πρέπει να σχεδιάσετε προσεκτικά και να εκτελέσετε τη μετάβαση καλά.

Μια ριζική αλλαγή θα σας αναγκάσει να προχωρήσετε ένα βήμα πιο πέρα ​​για να διασφαλίσετε ότι όλοι οι καταναλωτές όλων των εκδόσεων του API σας γνωρίζουν την αλλαγή.

Τύποι εκδόσεων API

Καθώς στρέφουμε σε διαφορετικούς τύπους εκδόσεων API, θα θέλαμε να χρησιμοποιήσουμε αυτές τις πληροφορίες σε διάφορα πεδία αλλαγών API για να αξιολογήσουμε τους τύπους. Κάθε προσέγγιση έχει το δικό της σύνολο πλεονεκτημάτων και αδυναμιών στην αντιμετώπιση αλλαγών με βάση το πεδίο εφαρμογής τους.

Υπάρχουν αρκετές μέθοδοι για τη διαχείριση της έκδοσης του API σας. Η έκδοση διαδρομής URI είναι η πιο κοινή.

Διαδρομή URI

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

This strategy involves putting the version number in the path of the URI, and is often done with the prefix "v". More often than not, API designers use it to refer to their application version (i.e. "trunk") rather than the endpoint version (i.e. "leaf" or "branch"), but that's not always a safe assumption.

URI path versioning implies orchestrated releases of application versions that will require one of two approaches: maintaining one version while developing a new one or forcing consumers to wait for new resources until the new version is released. It also means you'd need to carry over any non-changed endpoints from version to version. However, for APIs with relatively low volatility, it's still a decent option.

You would likely not want to relate your version number to that of the endpoint or resource, because it would easily result in something like a v4 of products but a v1 of variants, which would be rather confusing.

Query Params

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

This type of versioning adds a query param to the request that indicates the version. Very flexible in terms of requesting the version of the resource you'd like at the "leaf" level, but it holds no notion of the overall API's version and lends itself to the same out-of-sync issues mentioned in the above comment on endpoint-level versioning of the URI path.

Header

Accept: version=1.0

The header approach is one that provides more granularity in serving up the requested version of any given resource.

However, it's buried in the request object and isn't as transparent as the URI path option. It's also still hard to tell whether 1.0 refers to the version of the endpoint or the API itself.

Integrating Types

Each of these approaches seem to have the weakness of either favoring a "leaf" or "trunk" scope, but not supporting both.

If you need to maintain the overall API version and also provide support for multiple versions of resources, consider a blend of the URI Path and Query Params types, or a more advanced Header approach.

# URI path and query params combo //api.example.com/v1/products?version=1 //api.example.com/v1/products?version=2 # Extended headers, for //api.example.com/products Accept: api-version=1; resource-version=1 Accept: api-version=1; resource-version=2

Conclusion

We've covered a lot of ground here, so let's recap:

  • API versioning is the practice of transparently managing changes to your API.
  • Managing an API boils down to defining and evolving data contracts and dealing with breaking changes.
  • Ο πιο αποτελεσματικός τρόπος για να εξελιχθεί το API σας χωρίς να σπάσετε τις αλλαγές είναι να ακολουθήσετε αποτελεσματικές αρχές διαχείρισης αλλαγών API.
  • Για τα περισσότερα API, η έκδοση στο μονοπάτι URI είναι η πιο απλή λύση.
  • Για πιο σύνθετα ή ασταθή API, μπορείτε να διαχειριστείτε ποικίλα εύρη αλλαγών χρησιμοποιώντας μια ενοποίηση των μεθόδων URI path και query params.

Παρόλο που αυτές οι αρχές πρέπει να παρέχουν σαφή κατεύθυνση στον τρόπο αποτελεσματικής διαχείρισης της αλλαγής στα API σας, η ανάπτυξη ενός API είναι πιθανώς περισσότερο από μια τέχνη παρά μια επιστήμη. Απαιτεί σκέψη και προνοητικότητα για τη δημιουργία και τη διατήρηση ενός αξιόπιστου API.