Sådan opbygges Rest API med NodeJS, Express og MySQL

Med kendskab til JavaScript og MySQL kan vi opbygge vores NodeJS API ved hjælp af Express.

Jeg lavede noget research, og jeg forsøgte at udvikle et API fra bunden.
Jeg kan godt lide at forenkle tingene og forsøge at undgå kodedobling.

Denne guide vil vise dig, hvordan du opbygger et API fra bunden:
Du vil lære at oprette ruter,
at bruge mysql2, hvordan du konfigurerer og opretter forbindelse til databasen, og hvordan du kører forespørgsler med prepared statements.
Hvordan du opretter en middleware, der kan få et ekstra argument ud over req, res og next callback.
Du lærer, hvordan du kontrollerer dataene fra request-objektet ved hjælp af Express Validator-modulet.
Du lærer at bruge JWT-modulet til at oprette et token til brugeren, verificere tokenet og hente det objekt, der er gemt i tokenet.
Dertil kommer, at du lærer at give brugere adgangstilladelse til en bestemt rute baseret på deres brugerroller.

Teknologier og pakker:

  • NodeJS
  • Express
  • mysql2
  • bcryptjs
  • jsonwebtoken
  • express-validator
  • dotenv
  • cors

Installation af MySQL:

Jeg bruger WSL, og du kan bruge denne vejledning til at se, hvordan du installerer MySQL i WSL.
Du skal sikre dig, at MySQL kører med denne kommando:

sudo service mysql status
Enter fullscreen mode Exit fullscreen mode

Hvis den ikke kører, skal du bare bruge:

sudo service mysql status
Enter fullscreen mode Exit fullscreen mode

Hvis den ikke kører, skal du bare bruge:

sudo service mysql start
Gå ind i fuldskærmstilstand Afslut fuldskærmstilstand

Programoversigt:

Vi vil opbygge et rest-API til CRUD-operationer: oprette, læse, opdatere og slette brugere.

Opret projektmappen, og installer alle afhængigheder:

mkdir mysql-node-express && cd mysql-node-expressnpm init -ynpm i express express-validator mysql2 cors dotenv jsonwebtoken -Snpm i nodemon -D
Gå ind i fuldskærmstilstand Afslut fuldskærmstilstand

Gå til filen package.json, og ændr “main”-værdien til “src/server.js” og tilføj disse scripts til scripts-objektet:

"start": "node src/server.js","dev": "nodemon"
Enter fullscreen mode Exit fullscreen mode

package.json skal se sådan ud:

Opret .env-fil:

Vi vil bruge .env-filen til at administrere alle vores miljøvariabler.
Den .env-fil er en skjult fil, der giver os mulighed for at tilpasse vores miljøvariabler ved hjælp af ENV VARIABLE = VALUE-syntaksen.
Disse variabler indlæses ved hjælp af dotenv-modulet, som vi allerede har installeret.
Den .env-fil kan defineres på forskellige stadier af miljøet (udviklings- / stage- / produktionsmiljøer).

Opret .env-filen, kopier følgende linjer, og opdater filen med dit MySQL db_name, db_username og password:

# DB ConfigurationsHOST=localhostDB_USER=db_usernameDB_PASS=db_passwordDB_DATABASE=db_name# local runtime configsPORT=3000SECRET_JWT=supersecret
Enter fullscreen mode Exit fullscreen mode

Opret nodemon.json-filen:

Nodemon er et værktøj, der hjælper med at udvikle applikationer baseret på node.js ved automatisk at genstarte node-applikationen, når der registreres filændringer i målmappen.
Nodemon er en erstatningswrapper for node. I stedet for at bruge node-kommandoen skal vi bruge nodemon-kommandoen på kommandolinjen til at udføre vores script.
Vi kan nemt tilføje konfigurationsswitches, mens vi kører nodemon på kommandolinjen, f.eks.:

nodemon --watch src
Enter fullscreen mode Exit fullscreen mode

Vi kan også bruge en fil (nodemon.json) til at specificere alle skifterne.
Hvis vi ønsker at overvåge flere filer i en mappe, kan vi tilføje mappen i “watch”-arrayet.
Hvis vi ønsker at søge efter en bestemt udvidelse (f.eks. en ts-fil), kan vi bruge egenskaben “ext”.
Hvis vi ønsker at ignorere nogle filer, kan vi definere dem i arrayet “ignore”‘ osv….
Jeg bruger denne fil mest, når jeg opretter en server med NodeJS baseret på typescript, men jeg synes, det er nemmere at have flere steder at inkludere vores app-konfigurationer.
Denne fil er valgfri.

Opret filen nodemon.json, og tilføj dette til filen:

{ "watch": , "ext": ".js", "ignore": }
Enter fullscreen mode Exit fullscreen mode

Opret src-mappen:

mkdir src && cd src
Indtast fuldskærmstilstand Afslut fuldskærmstilstand

I src-mappen opretter du undermapper: controllers, models, routes, middleware, db og utils:

mkdir controllers models routes middleware db utils
Gå ind i fuldskærmstilstand Afslut fuldskærmstilstand

Opsætning af Express-server:

mkdir controllers models routes middleware db utils

I src-mappen opretter du filen server.js og kopierer disse linjer:

I denne fil importerer vi express til at opbygge rest-API’erne og bruger express.json() til at analysere indgående anmodninger med JSON-nytelaster.

Vi importerer også dotenv-modulet til at læse .env-konfigurationsfilen for at få portnummeret til at køre serveren.

Vi importerer også userRouter.

Efter det har vi en middleware, der håndterer 404-fejl → hvis nogen søger efter et endpoint, der ikke eksisterer, får de denne fejl: “Endpoint Not Found” med 404-statuskoden. Derefter bruger vi error middleware, som vil hente fejldata fra de tidligere ruter. hvis next(err) kaldes, kan du se 404 middleware som et eksempel.
Vi lytter til porten fra.env-filen og udskriver den til konsollen, at serveren kører.

Opret MySQL-database og brugertabel:

I db-mappen opretter vi filen create-user-db.sql og copy-paste disse linjer:

I dette script dropper vi først databasen, hvis den eksisterer, så den hurtigt kan nulstilles i tilfælde af en fejl (du kan kommentere denne linje, hvis du ønsker det), derefter opretter vi databasen, hvis den ikke eksisterer. Vi sætter den som vores aktive database og opretter en “user”-tabel med alle kolonnerne (id, brugernavn osv.), hvilket igen giver mulighed for en nem nulstilling, hvis det er nødvendigt. Du kan køre denne forespørgsel i din databaseklient, hvis du bruger en sådan.

Hvis du bruger wsl, kan du i db-mappen køre:

mysql -u -p < create-user-db.sql
Enter fullscreen mode Exit fullscreen mode

Configure and Connect to MySQL database:

Opret en ekstra fil i db-mappen kaldet db-connection.js, og copy-paste denne:

I denne fil importerer vi først dotenv-modulet og bruger det til at læse database-konfigurationsoplysninger som db host, db user fra .env-filen.

Vi kontrollerer forbindelsen, hvis der er et problem med databasen, og frigiver derefter forbindelsen.

Vi har en query-metode, der returnerer et løfte om resultatet af forespørgslen.

Vi bruger en try-catch-blok til at fange almindelige MySQL-fejl og returnere passende HTTP-statuskoder og -meddelelser.

I slutningen af filen opretter vi en instans af DBConnection-klassen og bruger query-metoden, og i model.js (som vi ser i næste trin) bruger vi query-metoden igen.

Opret fejlhåndtering:

Næst skal vi oprette vores fejlbehandler.

Det gør vi ved først at oprette filen HttpException.utils.js under utils-mappen og copy-paste følgende:

HttpException-klassen arver Error-klassen.
Konstruktøren henter status, besked og data. Vi videregiver beskedvariablen til den overordnede konstruktør ved hjælp af super(message), og derefter initialiserer vi instansvariablerne status, besked og data.

Derpå opretter vi en middleware-fejlhåndtering i middleware-mappen.
Vi opretter en fejl. middleware.js-fil og copy-paste følgende:

Vi kan se nederst i filen, hvordan objektet bliver.

Middlewaren vil få req, res og next callback, men den vil også få et ekstra argument, error (ved at bruge next(error), før vi kommer til denne middleware).

Vi bruger destructuring til at hente variablerne fra error-objektet og indstiller status til 500, hvis det ikke er blevet konfigureret før.

Efter dette, uanset om status er 500, sørger vi for at ændre meddelelsen, så brugeren modtager en generisk intern serverfejlmeddelelse uden at afsløre den nøjagtige karakter af fejlen.

Efter det opretter vi et fejlobjekt med egenskaberne type, status og besked (data er valgfri).

Opret utils (hjælpefiler):

I utils-mappen opretter vi yderligere to filer, common.utils.js og userRoles.utils.js.

common.utils.js:

Denne funktion hjælper med at indstille flere felter til forberedte forespørgsler med nøgle-værdipar.
ColumnSet arrayet af nøgle =? par,
Værdierne skal derfor være i samme rækkefølge som columnSet arrayet.

userRoles.utils.js:

Enter fullscreen mode Exit fullscreen mode

Opret Async-funktion:

Opret en anden fil kaldet awaitHandlerFactory.middleware.js i middleware-mappen, og copy-paste dette:

Generelt ved vi, at middleware kun er en asynkron metode, der får req, res og next-argumenterne, så hvis vi ønsker, at denne middleware skal få et ekstra argument, gør vi det på denne måde (vi bruger også dette i auth-middlewaren i næste trin).

Denne funktion vil få en callback, køre middlewarescriptet og vil forsøge at udløse denne callback i try-blokken.
Hvis noget går galt her, vil den fange fejlen, og vi vil bruge next(err) (som vil overføre den til den næste middleware => error.middleware.js).

Opret autentifikations-middleware:

En anden middleware, som vi har brug for, er auth middleware, som vi vil bruge til at kontrollere brugertilladelser via JWT-modulet.

Som awaitHandlerFactory.middleware.js middleware, har vi en middleware her, der kræver yderligere argument (som er valgfrit) => roller.

Jeg brugte try-catch til at justere fejlstatus i catch-området til 401 (hvis tokenet f.eks. er udløbet).

I første omgang leder vi efter req.headers.authorization – om det ikke er defineret i headeren, eller hvis headeren ikke starter med “Bearer “, vil brugeren modtage et 401-svar. Hvis den begynder med “Bearer “, henter vi tokenet og bruger den hemmelige nøgle fra .env-filen til at dechifrere det.

Vi verificerer tokenet ved at bruge den synkrone jwt.verify-funktion, der henter tokenet og secretKey som argumenter og returnerer den afkodede nyttelast, om signaturen er gyldig, og om de valgfrie felter expiration, audience eller issuer er gyldige. Ellers vil den kaste en fejl.

Nu kan vi finde brugeren med dette token ved at søge på bruger-id.
Hvis brugeren ikke længere eksisterer, vil de få en undtagelse på 401 uden nogen oplysninger.
Hvis brugeren eksisterer, vil vi kontrollere, om den aktuelle bruger er ejeren, der søger efter sine ruter, eller om brugeren har rollen til at få adgang til denne rute.
Vi gemmer den aktuelle bruger, bare i tilfælde af at han ønsker at få sine data på den næste middleware (som “whoami”-ruten).

Datavalidering ved hjælp af Express Validator-modulet:

I middleware-mappen opretter vi en ekstra fil, som vi skal bruge til at verificere req.body-egenskaberne.

Opret en undermappe i middleware-mappen kaldet validators, og opret en fil i denne mappe, userValidator.middleware.js. Copy-paste dette:

I denne fil har jeg brugt express-validator-modulet, som er meget nemt at bruge, når vi skal kontrollere nogle egenskaber, kontrollere, om egenskaben eksisterer, eller oprette brugerdefinerede kontroller med en brugerdefineret besked til brugeren, hvis en egenskabsværdi ikke er gyldig.

Nu kan vi begynde at oprette vores rute-, controller- og modelfiler.

Definer ruter:

Opret filen user.route.js i mappen routes, og copy-paste dette:

Eksemplet ovenfor viser, hvordan man definerer ruter. Lad os prøve at bryde det ned i stykker:

  • Du kan oprette en router ved hjælp af express.Router(). hver rute kan indlæse en middleware-funktion, der håndterer forretningslogikken. userController bærer f.eks. alle de vigtigste middlewares.For at bruge routeren skal routeren eksporteres som et modul og bruges i hovedappen ved hjælp af app.use(router_module).
  • Vi brugte auth middleware til brugergodkendelse og autorisering, til kontrol af brugertoken eller brugerrolle for ruten.I vores eksempel bruger nogle af ruterne auth-middleware til at kontrollere brugergodkendelse og -autorisering. denne middleware udløses før den vigtigste middleware (den, der indeholder forretningslogikken). næste callback skal kaldes for at videregive kontrol til den næste middleware-metode. ellers vil anmodningen blive hængende.
  • awaitHandlerFactory (try-catch middleware) bruges til at indpakke al den asynkrone middleware. På denne måde vil awaitHandlerFactory fange fejlen, hvis en af middlewares kaster en fejl. du kan se, at alle vores middleware-funktioner er indpakket med awaitHandlerFactory middleware, hvilket hjælper os med at håndtere vores fejl ved at bruge try-catch ét sted.
  • Dertil kommer, at vi har skemaerne createUserSchema, updateUserSchema og validateLogin til at validere kroppen, før vi starter den næste middleware.

Syntaksen for HTTP-metoden er:

Opret controller:

Opret filen user.controller.js i controllers-mappen, og copy-paste dette:

Som nævnt ovenfor indeholder controller-filen vores forretningslogik til håndtering af vores ruter.
I vores eksempel bruger nogle metoder UserModel-klassen til at forespørge i databasen for at få dataene.
For at returnere dataene i hver middleware bruger vi res.send(result) til at sende et svar til klienten.

Opret modellen:

Opret filen user.model.js i mappen models, og copy-paste dette:

Denne klasse opretter forbindelsen mellem controlleren og databasen.
Her har vi alle de metoder, der får argumenterne fra controlleren, laver en forespørgsel, forbereder udsagn, opretter forbindelse til databasen ved hjælp af forespørgselsmetoden fra db-connection-klassen, sender forespørgslen med arrayet med forberedte udsagn og får resultatet tilbage.
Hver funktion returnerer resultatet til controlleren.

.gitIgnore:

Hvis du beslutter dig for at tilføje dette projekt til din GitHub, så glem ikke at oprette en .gitignore-fil og copy-paste denne:

node_modules.env
Enter fullscreen mode Exit fullscreen mode

Denne fil fortæller bare git, hvilke filer den skal ignorere.
Du bør undgå node_modules-mappen, fordi den er tung og ikke nødvendig for repositoriet.
Når nogen kloner dette repository, vil de bruge kommandoen “npm I” til at installere alle afhængighederne.
Det at ignorere .env-filen er for at skjule dine private konfigurationer for andre udviklere, der bruger din kode.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret.