RESTful API Design Med NodeJS & Restify

Hva du skal skape

Den RESTful API består av to hovedkonsepter: Ressurs, og Representasjon. Ressurs kan være ethvert objekt knyttet til data, eller identifisert med en URI (flere enn en URI kan referere til samme ressurs), og kan betjenes ved hjelp av HTTP-metoder. Representasjon er måten du viser ressursen på. I denne opplæringen vil vi dekke noen teoretisk informasjon om RESTful API-design, og implementere et eksempel på blogging-applikasjons-API ved å bruke NodeJS.

Ressurs

Å velge de riktige ressursene for en RESTful API er en viktig del av design. Først av alt må du analysere forretningsdomenet ditt og bestemme hvor mange og hva slags ressurser som skal brukes som er relevante for din forretningsbehov. Hvis du designer en blogging-API, vil du sannsynligvis bruke Artikkel, Bruker, og Kommentar. Det er ressursnavnene, og dataene knyttet til det er selve ressursen:

"title": "Hvordan utforme RESTful API", "innhold": "RESTful API-design er et svært viktig tilfelle i programvareutviklingsverdenen.", "forfatter": "huseyinbabal", "tags": ["technology" , "nodejs", "node-restify"] "category": "NodeJS"

Ressursord

Du kan fortsette med en ressursoperasjon etter at du har bestemt deg for de nødvendige ressursene. Drift her refererer til HTTP-metoder. For eksempel, for å opprette en artikkel, kan du gjøre følgende forespørsel:

POST / artikler HTTP / 1.1 Host: localhost: 3000 Innholdstype: application / json "title": "RESTful API Design med Restify", "slug": "restful-api-design-with-restify" : "Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.", "Forfatter": "huseyinbabal"

På samme måte kan du se en eksisterende artikkel ved å utstede følgende forespørsel:

GET / articles / 123456789012 HTTP / 1.1 Host: localhost: 3000 Innholdstype: application / json

Hva med å oppdatere en eksisterende artikkel? Jeg kan høre at du sier:

Jeg kan gjøre en annen POST-forespørsel til / artikler / oppdatering / 123456789012 med nyttelastet.

Kanskje foretrukket, men URI blir mer kompleks. Som vi sa tidligere, kan operasjoner henvise til HTTP-metoder. Dette betyr, angi Oppdater operasjon i HTTP-metoden i stedet for å sette det i URI. For eksempel:

PUT / artikler / 123456789012 HTTP / 1.1 Host: localhost: 3000 Content-Type: application / json "title": "Oppdatert hvordan du utformer RESTful API", "innhold": "Oppdatert RESTful API-design er et svært viktig tilfelle i programvareutviklingsverden. "," forfatter ":" huseyinbabal "," tags ": [" teknologi "," nodejs "," restify "," one more tag "]" category ":" NodeJS "

Forresten, i dette eksemplet ser du koder og kategorivelter. De trenger ikke å være obligatoriske felt. Du kan la dem være tomme og sette dem i fremtiden. 

Noen ganger må du slette en artikkel når den er utdatert. I så fall kan du bruke en SLETT HTTP-forespørsel til / artikler / 123456789012.

HTTP-metoder er standardkoncepter. Hvis du bruker dem som en operasjon, vil du ha enkle URIer, og denne typen enkel API vil hjelpe deg med å få lykkelige forbrukere.

Hva om du vil legge inn en kommentar til en artikkel? Du kan velge artikkelen og legge til en ny kommentar til den valgte artikkelen. Ved å bruke denne setningen kan du bruke følgende forespørsel:

POST / artikler / 123456789012 / kommentarer HTTP / 1.1 Vertskap: localhost: 3000 Innholdstype: application / json "text": "Wow! Dette er en god opplæring", "forfatter": "John Doe"

Ovennevnte form for ressurs kalles som en sub-ressurs. Kommentar er en del ressurs av Artikkel. De Kommentar nyttelast over vil bli satt inn i databasen som barn av Artikkel. Noen ganger refererer en annen URI til den samme ressursen. For eksempel, for å vise en bestemt kommentar, kan du bruke enten:

GET / articles / 123456789012 / comments / 123 HTTP / 1.1 Vertsområde: localhost: 3000 Innholdstype: søknad / json 

eller:

GET / comments / 123456789012 HTTP / 1.1 Vert: localhost: 3000 Content-Type: application / json

versjons~~POS=TRUNC

Generelt endrer API-funksjonene ofte for å gi nye funksjoner til forbrukerne. I så fall kan to versjoner av samme API eksistere samtidig. For å skille disse to funksjonene, kan du bruke versjonering. Det finnes to former for versjonering

  1. Versjon i URI: Du kan oppgi versjonsnummeret i URI. For eksempel, /v1.1/articles/123456789012.
  2. Versjon i overskrift: Oppgi versjonsnummeret i overskriften, og aldri endre URI.For eksempel:
GET / articles / 123456789012 HTTP / 1.1 Vertsområde: localhost: 3000 Accept-Version: 1.0

Faktisk endrer versjonen bare representasjonen av ressursen, ikke konseptet av ressursen. Så, du trenger ikke å endre URI-strukturen. I v1.1, kanskje et nytt felt ble lagt til artikkelen. Men det returnerer fortsatt en artikkel. I det andre alternativet er URI fortsatt enkelt, og forbrukerne trenger ikke å endre deres URI på klient-side implementeringer. 

Det er viktig å utforme en strategi for situasjoner der forbrukeren ikke oppgir et versjonsnummer. Du kan opprette en feil når versjon ikke er oppgitt, eller du kan returnere et svar ved å bruke den første versjonen. Hvis du bruker den nyeste stabile versjonen som standard, kan forbrukerne få mange feil for deres implementeringer på klientsiden.

Representasjon

Representasjon er måten en API viser ressursen på. Når du ringer et API-endepunkt, vil du få tilbake en ressurs. Denne ressursen kan være i et hvilket som helst format som XML, JSON, etc. JSON er å foretrekke hvis du designer en ny API. Men hvis du oppdaterer en eksisterende API som pleide å returnere et XML-svar, kan du gi en annen versjon for et JSON-svar. 

Det er nok teoretisk informasjon om RESTful API-design. La oss se på bruk av virkelige liv ved å designe og implementere en Blogging API ved hjelp av Restify.

Blogging REST API

Design

For å designe en RESTful API må vi analysere bedriftsdomene. Da kan vi definere ressursene våre. I en Blogging API trenger vi:

  • Opprett, oppdater, slett, vis Artikkel
  • Lag en kommentar til en bestemt Artikkel, Oppdater, Slett, Vis, Kommentar
  • Opprett, oppdater, slett, vis Bruker

I denne API-en vil jeg ikke dekke hvordan du autentiserer en bruker for å opprette en artikkel eller kommentar. For godkjenningsdelen kan du referere til Token-basert godkjenning med AngularJS og NodeJS opplæring. 

Våre ressursnavn er klare. Ressursoperasjoner er rett og slett CRUD. Du kan referere til følgende tabell for en generell presentasjon av API.

Ressursnavn HTTP Verbs HTTP-metoder
Artikkel opprett artikkel
oppdater artikkelen
slett artikkel
se artikkelen
POST / artikler med nyttelast
PUT / artikler / 123 med nyttelast
SLETT / artikler / 123
GET / artikkel / 123
Kommentar opprett kommentar
oppdater Coment
slett kommentar
se kommentar
POST / artikler / 123 / kommentarer med nyttelast
PUT / kommentarer / 123 med nyttelast
SLETT / kommentarer / 123
GET / kommentarer / 123
Bruker Opprett bruker
oppdater brukeren
Slett bruker
se brukeren
POST / brukere med nyttelast
PUT / brukere / 123 med nyttelast
SLETT / brukere / 123
GET / users / 123

Prosjektoppsett

I dette prosjektet vil vi bruke NodeJS med Restify. Ressursene vil bli lagret i MongoDB database. Først av alt kan vi definere ressurser som modeller i Restify.

Artikkel

var mongoose = krever ("mongoose"); var Schema = mongoose.Schema; var ArticleSchema = nytt skjema (tittel: String, slug: String, innhold: String, forfatter: type: String, ref: "User")); mongoose.model ('Article', ArticleSchema);

Kommentar

var mongoose = krever ("mongoose"); var Schema = mongoose.Schema; var CommentSchema = nytt skjema (tekst: String, artikkel: type: String, ref: "Artikkel", forfatter: type: String, ref: "User"); mongoose.model ('Comment', CommentSchema);

Bruker

Det vil ikke være noen operasjon for brukerressursen. Vi antar at vi allerede kjenner den nåværende brukeren som vil kunne operere på artikler eller kommentarer.

Du kan spørre hvor denne mongoose-modulen kommer fra. Det er det mest populære ORM-rammeverket for MongoDB skrevet som en NodeJS-modul. Denne modulen er inkludert i prosjektet i en annen config-fil. 

Nå kan vi definere våre HTTP-verker for de ovennevnte ressursene. Du kan se følgende:

var restify = require ('restify'), fs = krever ('fs') var controllers = , controllers_path = process.cwd () + '/ app / controllers'fs.readdirSync (controllers_path) .forEach (funksjon ) if (file.indexOf ('. js')! = -1) controllers [file.split ('.') [0]] = krever (controllers_path + '/' + fil)) var server = restify.createServer (); server .use (restify.fullResponse ()) .use (restify.bodyParser ()) // Artikkel Start server.post ("/ articles", controllers.article.createArticle) server.put ("/ articles /: id" controllers.article.updateArticle) server.del ("/ articles /: id", controllers.article.deleteArticle) server.get (bane: "/ articles /: id", versjon: "1.0.0", kontroller. article.viewArticle) server.get (path: "/ articles /: id", versjon: "2.0.0", controllers.article.viewArticle_v2) // Artikkel End // Kommentar Start server.post ("/ comments" server.del ("/ kommentarer /: id", controllers.comment.deleteComment) server.get ("/ kommentarer /: id ", controllers.comment.viewComment) // Kommentar End var port = process.env.PORT || 3000; server.listen (port, funksjon (err) hvis (err) console.error (err) else console.log ('App er klar til:' + port)) hvis (process.env.environment == 'production' ) process.on ('uncaughtException', funksjon (err) console.error (JSON.parse (JSON.stringify (feil, ['stakk', 'melding', 'indre'], 2)))))

I denne kodestykket blir først og fremst styringsfilene som inneholder kontrolleringsmetoder iterated og alle kontrollerne initialiseres for å utføre en bestemt forespørsel til URI. Deretter defineres URIer for bestemte operasjoner for grunnleggende CRUD-operasjoner. Det finnes også versjonering for en av operasjonene på artikkelen. 

For eksempel, hvis du oppgir versjon som 2 i Accept-Version header, viewArticle_v2 vil bli utført. viewArticle og viewArticle_v2 Begge gjør den samme jobben, og viser ressursen, men de viser artikkelressurs i et annet format, som du kan se i tittel felt nedenfor. Endelig er serveren startet på en bestemt port, og noen feilrapporteringskontroller blir brukt. Vi kan fortsette med kontrolleringsmetoder for HTTP-operasjoner på ressurser.

article.js

var mongoose = krever ('mongoose'), Artikkel = mongoose.model ("Artikkel"), ObjectId = mongoose.Types.ObjectId exports.createArticle = funksjon (req, res, neste) var articleModel = ny artikkel ); articleModel.save (funksjon (feil, artikkel) hvis (err) res.status (500); res.json (type: false, data: "Feil oppstod:" + err) else res.json type: sann, data: artikkel)) exportss.viewArticle = funksjon (req, res, neste) Article.findById (new ObjectId (req.params.id) err) res.status (500); res.json (type: false, data: "Feil oppstod:" + err) annet hvis (artikkel) res.json (type: true, data: article ) else res.json (type: false, data: "Artikkel:" + req.params.id + "ikke funnet")) exportss.viewArticle_v2 = funksjon (req, res, neste) Article.findById (new ObjectId (req.params.id), funksjon (feil, artikkel) if (err) res.status (500); res.json (type: false, data: "Feil oppstod:" + err) annet hvis (artikkel) article.title = article.title + "v2" res.json (type: true, data: article) else res.json : "Artikkel:" + req.params.id + "ikke funnet")) exports.updateArticle = funksjon (req, res, neste) var updatedArticleModel = ny artikkel (req.body); Article.findByIdAndUpdate (new ObjectId (req.params.id), updatedArticleModel, funksjon (feil, artikkel) if (err) res.status (500); res.json (type: false, data: "Feil oppstod: "+ err) annet hvis (artikkel) res.json (type: sann, data: artikkel) else res.json (type: false, data:" Article: "+ req.params. id = "ikke funnet")) exports.deleteArticle = funksjon (req, res, neste) Article.findByIdAndRemove (nytt objekt (req.params.id), funksjon (feil, artikkel)  ) res.json (type: false, data: "Feil oppsto:" + err) else res.json (type: true, data: "Artikkel:" + req. params.id + "slettet med hell")) 

Du finner en forklaring på grunnleggende CRUD-operasjoner på Mongoose-siden nedenfor:

  • createArticle: Dette er en enkel lagre operasjon på articleModel sendt fra forespørselsorganet. En ny modell kan opprettes ved å sende forespørselslegemet som en konstruktør til en modell som var articleModel = ny artikkel (req.body)
  • viewArticle: For å kunne se artikkeldetaljer, er det nødvendig med en artikkel-ID i URL-parameteren. Finn én med en ID parameter er nok til å returnere artikkel detalj.
  • updateArticle: Artikkeloppdatering er et enkelt søk og noe datamanipulering på den returnerte artikkelen. Til slutt må den oppdaterte modellen lagres i databasen ved å utstede en lagre kommando.
  • deleteArticle: findByIdAndRemove er den beste måten å slette en artikkel ved å gi artikkelen ID.

Mongoose kommandoene nevnt ovenfor er ganske enkelt statisk som metode gjennom Artikkel objekt som også er en referanse til Mongoose-skjemaet.

comment.js

var mongoose = krever ('mongoose'), Kommentar = mongoose.model ("Kommentar"), Artikkel = mongoose.model ("Artikkel"), ObjectId = mongoose.Types.ObjectId exports.viewComment = funksjon (req, res)  Artikkel.findOne ("comments._id": nytt ObjectId (req.params.id), "kommentarer. $": 1, funksjon (feil, kommentar) hvis (err) res.status (500) ; res.json (type: false, data: "Feil oppstod:" + err) annet hvis (kommentar) res.json (type: true, data: new Comment (comment.comments [0]) ) andre res.json (type: false, data: "Kommentar:" + req.params.id + "ikke funnet")) exportss.updateComment = funksjon (req, res, neste) var updatedCommentModel = ny kommentar (req.body); console.log (updatedCommentModel) Article.update ("comments._id": new ObjectId (req.params.id), "$ set": "kommentarer. $. tekst": updatedCommentModel.text, "kommentarer. $ .author ": updatedCommentModel.author, funksjon (err) hvis (err) res.status (500); res.json (type: falsk, data:" Feil oppstod: "+ feil)) res.json (type: true, data: "Kommentar:" + req.params.id + "updated")) Exports.deleteComment = funksjon (req, res, neste) Article.findOneAndUpdate  "comments._id": new ObjectId (req.params.id), "$ pull": "kommentarer": "_id": nytt ObjectId (req.params.id), funksjon (feil, artikkel) if (err) res.status (500); res.json (type: false, data: "Feil oppstod:" + err) else if (article) res.json sann, data: artikkel) annet res.json (type: falsk, data: "Kommentar:" + req.params.id + "ikke funnet"))

Når du gjør en forespørsel til en av ressurs-URIene, vil den relaterte funksjonen som er oppgitt i kontrolleren, bli utført. Hver funksjon inne i kontrollerfilene kan bruke req og res objekter. De kommentar ressurs her er en del ressurs av Artikkel. Alle forespørselsoperasjoner er laget gjennom artikkelmodellen for å finne et underdokument og foreta den nødvendige oppdateringen. Men når du prøver å vise en kommentar ressurs, vil du se en, selv om det ikke er noen samling i MongoDB.  

Andre designforslag

  • Velg enkle å forstå ressurser for å gi enkel bruk til forbrukerne.
  • La virksomhetslogikken bli implementert av forbrukerne. For eksempel har artikkelen ressurs et felt som heter slug. Forbrukerne trenger ikke å sende denne detaljen til REST API. Denne slug-strategien bør håndtere på REST API-siden for å redusere koblingen mellom API og forbrukere. Forbrukerne trenger bare å sende titteldetaljer, og du kan generere sluggen i henhold til dine forretningsbehov på REST API-siden.
  • Implementer et autorisasjonslag for API-endepunktene dine. Uautoriserte forbrukere kan få tilgang til begrensede data som tilhører en annen bruker. I denne opplæringen dekker vi ikke brukerressursen, men du kan referere til Token-basert godkjenning med AngularJS og NodeJS for mer informasjon om API-godkjenninger.
  • Bruker URI i stedet for spørringsstreng. / artikler / 123  (Flink), / Artikler? Id = 123 (Bad).
  • Ikke hold staten; Bruk alltid øyeblikkelig inngang / utgang.
  • Bruk substantiv for ressursene dine. Du kan bruke HTTP-metoder for å kunne bruke ressurser.

Til slutt, hvis du designer en RESTful API ved å følge disse grunnleggende reglene, vil du alltid ha et fleksibelt, vedlikeholdbart, lettforståelig system.