Hur man bygger Rest API med NodeJS, Express och MySQL

Med kunskap om JavaScript och MySQL kan vi bygga vårt NodeJS API med hjälp av Express.

Jag gjorde en del forskning och försökte utveckla ett API från grunden.
Jag gillar att förenkla saker och försöker undvika koddubblering.

Denna guide kommer att visa dig hur du bygger ett API från grunden:
Du kommer att lära dig hur man skapar rutter,
hur man använder mysql2, hur man konfigurerar och ansluter till databasen och hur man kör frågor med förberedda uttalanden.
Hur man skapar en middleware som kan få ett ytterligare argument förutom req, res och next callback.
Du kommer att lära dig hur man kontrollerar data från förfrågningsobjektet med hjälp av modulen Express Validator.
Du lär dig hur du använder JWT-modulen för att skapa en token för användaren, verifiera tokenet och hämta objektet som är lagrat i tokenet.
Du lär dig dessutom hur du ger användarna behörighet att få tillgång till en viss rutt baserat på deras användarroller.

Teknik och paket:

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

Installation av MySQL:

Jag använder WSL, och du kan använda den här handledningen för att se hur du installerar MySQL i WSL.
Du måste se till att MySQL körs med det här kommandot:

sudo service mysql status
Enter fullscreen mode Exit fullscreen mode

Om det inte körs, använd bara:

sudo service mysql start
Gå in i helskärmsläge Avsluta helskärmsläge

Programöversikt:

Vi ska bygga ett rest-API för CRUD-operationer: skapa, läsa, uppdatera och ta bort användare.

Skapa projektmappen och installera alla beroenden:

mkdir mysql-node-express && cd mysql-node-expressnpm init -ynpm i express express-validator mysql2 cors dotenv jsonwebtoken -Snpm i nodemon -D
Gå in i fullskärmsläget Gå ut ur fullskärmsläget

Gå till filen package.json och ändra värdet “main” till “src/server.js” och lägg till dessa skript i skriptobjektet:

"start": "node src/server.js","dev": "nodemon"
Gå in i fullskärmsläge Avsluta fullskärmsläge

package.json ska se ut så här:

Skapa .env-fil:

Vi kommer att använda .env-filen för att hantera alla våra miljövariabler.
Den .env-filen är en dold fil som gör att vi kan anpassa våra miljövariabler med hjälp av syntaxen ENV VARIABLE = VALUE.
Dessa variabler laddas med hjälp av dotenv-modulen som vi redan har installerat.
Den .env-filen kan definieras i olika stadier av miljön (utvecklings-/stadie-/produktionsmiljöer).

Skapa .env-filen, kopiera följande rader och uppdatera filen med ditt MySQL db_name, db_username och lösenord:

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

Skapa filen nodemon.json:

Nodemon är ett verktyg som hjälper till att utveckla program baserade på node.js genom att automatiskt starta om nodeprogrammet när filändringar upptäcks i målkatalogen.
Nodemon är en ersättningsomslag för node. Istället för att använda kommandot node bör vi använda kommandot nodemon på kommandoraden för att exekvera vårt skript.
Vi kan enkelt lägga till konfigurationsbrytare när vi kör nodemon på kommandoraden, t.ex.:

nodemon --watch src
Enter fullscreen mode Exit fullscreen mode

Vi kan också använda en fil (nodemon.json) för att ange alla växlar.
Om vi vill titta på flera filer i en katalog kan vi lägga till katalogen i arrayen “watch”.
Om vi vill söka efter ett visst tillägg (t.ex. en ts-fil) kan vi använda egenskapen “ext”.
Om vi vill ignorera vissa filer kan vi definiera dem i arrayen “ignore”‘, och så vidare….
Jag använder den här filen mestadels när jag skapar en server med NodeJS som bygger på typescript, men jag tror att det är lättare att ha fler ställen att inkludera våra appkonfigurationer.
Den här filen är frivillig.

Skapa filen nodemon.json och lägg till detta i filen:

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

Skapa mappen src:

mkdir src && cd src
Gå in i fullskärmsläge Avsluta fullskärmsläge

I mappen src skapas undermappar: controllers, models, routes, middleware, db och utils:

mkdir controllers models routes middleware db utils
Gå in i fullskärmsläge Avsluta fullskärmsläge

Konfigurera Express-server:

I src-katalogen skapar du filen server.js och kopierar dessa rader:

I den här filen importerar vi express för att bygga rest-API:erna och använder express.json() för att analysera inkommande förfrågningar med JSON-nyttolaster.

Vi importerar också dotenv-modulen för att läsa .env-konfigurationsfilen för att få fram portnumret för att köra servern.

Vi importerar också userRouter.

Efter det har vi en middleware som hanterar 404-fel → om någon letar efter en slutpunkt som inte existerar kommer de att få det här felet: “Endpoint Not Found” med statuskoden 404. Efter det använder vi error middleware som hämtar feldata från de tidigare vägarna. om next(err) anropas kan du se 404 middleware som exempel.
Vi lyssnar på porten från filen.env och skriver ut den till konsolen att servern körs.

Skapa MySQL-databas och användartabell:

I db-katalogen skapar vi filen create-user-db.sql och kopierar och klistrar in dessa rader:

I det här skriptet släpper vi först databasen om den existerar så att den snabbt kan återställas vid ett misstag (du kan kommentera den raden om du vill), sedan skapar vi databasen om den inte finns. Vi ställer in den som vår aktiva databas och skapar en “user”-tabell med alla kolumner (id, användarnamn och så vidare), vilket återigen möjliggör en bekväm återställning om det behövs. Du kan köra denna fråga i din databasklient om du använder en sådan.

Om du använder wsl kan du i db-katalogen köra:

mysql -u -p < create-user-db.sql
Gå in i fullskärmsläge Avsluta fullskärmsläge

Konfigurera och ansluta till MySQL-databasen:

Skapa ytterligare en fil i db-katalogen som heter db-connection.js och kopiera och klistra in den här:

I den här filen importerar vi först dotenv-modulen och använder den för att läsa information om databasens konfiguration, t.ex. db-värd och db-användare, från env-filen.

Vi kontrollerar anslutningen om det skulle vara något problem med databasen och släpper sedan anslutningen.

Vi har en query-metod som returnerar ett löfte om resultatet av sökningen.

Vi använder ett try-catch-block för att fånga upp vanliga MySQL-fel och returnera lämpliga HTTP-statuskoder och meddelanden.

I slutet av filen skapar vi en instans av DBConnection-klassen och använder query-metoden och i model.js (som vi kommer att se i nästa steg) kommer vi att använda query-metoden igen.

Skapa felhanterare:

Nästan ska vi skapa vår felhanterare.

För att göra det skapar vi först filen HttpException.utils.js i utils-katalogen och kopierar och klistrar in följande:

Klassen HttpException ärver klassen Error.
Konstruktören hämtar status, meddelande och data. Vi kommer att skicka meddelandevariabeln till den överordnade konstruktören med super(message), och sedan kommer vi att initialisera instansvariablerna status, meddelande och data.

Efter det kommer vi att skapa en middleware-felhanterare i middleware-katalogen.
Vi kommer att skapa en error. middleware.js-filen och kopierar in följande:

Vi kan se längst ner i filen hur objektet kommer att se ut.

Middelfunktionen kommer att få req, res och next callback, men den kommer också att få ytterligare ett argument, error (genom att använda next(error) innan vi kommer till denna middleware).

Vi använder destructuring för att hämta variablerna från error-objektet och ställer in statusen till 500 om den inte har konfigurerats tidigare.

Efter detta, oavsett om statusen är 500, ser vi till att ändra meddelandet så att användaren får ett generiskt internt serverfelmeddelande utan att avslöja felets exakta karaktär.

Efter det skapar vi ett felobjekt med egenskaperna typ, status och meddelande (data är valfritt).

Skapa utils-filer (hjälpfiler):

I utils-katalogen skapar vi ytterligare två filer, common.utils.js och userRoles.utils.js.

common.utils.js:

Denna funktion hjälper till att ställa in flera fält för förberedda frågor med nyckel- och värdepar.
ColumnSet matrisen med nyckel =?-par,
Värdena bör därför vara i samma ordning som columnSet-matrisen.

userRoles.utils.js:

Enter fullscreen mode Exit fullscreen mode

Create Async function:

Skapa en annan fil som heter awaitHandlerFactory.middleware.js i middleware-katalogen och kopiera och klistra in det här:

I allmänhet vet vi att middleware bara är en asynkron metod som hämtar argumenten req, res och next, så om vi vill att den här middleware ska hämta ytterligare ett argument gör vi det på det här sättet (vi kommer att använda det här i auth-middleware också i nästa steg).

Denna funktion hämtar en callback, kör middleware-skriptet och försöker utlösa denna callback i try-blocket.
Om något går fel här kommer den att fånga upp felet och vi använder next(err) (som överför det till nästa middleware => error.middleware.js).

Skapa autentiserings-mellanvara:

En annan middleware som vi behöver är auth middleware som vi kommer att använda för att kontrollera användarbehörigheter via JWT-modulen.

Som liknar awaitHandlerFactory.middleware.js middleware, har vi en middleware här som kräver ytterligare argument (som är valfritt) => roles.

Jag använde try-catch för att justera felstatusen i fångstområdet till 401 (om token har löpt ut, till exempel).

I början letar vi efter req.headers.authorization – om det inte är definierat i huvudet eller om huvudet inte börjar med “Bearer “, kommer användaren att få ett 401-svar. Om det börjar med “Bearer ” hämtar vi token och använder den hemliga nyckeln från filen.env för att dechiffrera den.

Vi verifierar token genom att använda den synkrona funktionen jwt.verify, som hämtar token och secretKey som argument och returnerar den avkodade nyttolasten, om signaturen är giltig och om de valfria fälten för utgångsdatum, publik eller utfärdare är giltiga. I annat fall kommer den att kasta ett fel.

Nu kan vi hitta användaren med denna token genom att söka på användar-id.
Om användaren inte längre existerar kommer de att få ett undantag på 401 utan någon information.
Om användaren finns kommer vi att kontrollera om den aktuella användaren är ägaren som söker efter sina rutter eller om användaren har rollen för att få tillgång till den här rutten.
Vi sparar den aktuella användaren ifall han vill hämta sina data på nästa middleware (som “whoami”-rutten).

Datavalidering med hjälp av Express Validator-modulen:

I middleware-katalogen kommer vi att skapa ytterligare en fil som vi kommer att använda för att verifiera egenskaperna i req.body.

Skapa en undermapp i middleware-katalogen som heter validators och skapa en fil i den här katalogen, userValidator.middleware.js. Kopiera-klistra in detta:

I den här filen använde jag modulen express-validator, som är mycket enkel att använda när vi behöver kontrollera vissa egenskaper, kontrollera om egenskapen finns eller skapa anpassade kontroller med ett anpassat meddelande till användaren om något egenskapsvärde inte är giltigt.

Nu kan vi börja skapa våra filer för rutter, kontrollanter och modeller.

Definiera rutter:

Skapa filen user.route.js i katalogen routes och kopiera och klistra in följande:

Exemplet ovan visar hur man definierar rutter. Låt oss försöka bryta ner det i bitar:

  • Du kan skapa en router med hjälp av express.Router(). varje rutt kan ladda en middleware-funktion som hanterar affärslogiken. userController, till exempel, bär alla de viktigaste middlewares.För att använda routern ska routern exporteras som en modul och användas i huvudappen med app.use(router_module).
  • Vi använde auth middleware för användarautentisering och auktorisering, för att kontrollera användartoken eller användarroll för rutten.I vårt exempel använder några av rutterna auth middleware för att kontrollera användarautentisering och auktorisering. denna middleware kommer att utlösas före den huvudsakliga middleware (den som innehåller affärslogiken). nästa callback måste anropas för att överlämna kontrollen till nästa middleware-metod. annars kommer begäran att lämnas hängande.
  • awaitHandlerFactory (try-catch middleware) används för att svepa in alla asynkrona middleware. På så sätt kommer awaitHandlerFactory att fånga upp felet om en av middleware-funktionerna kastar ett fel.Du kan se att alla våra middleware-funktioner är förpackade med awaitHandlerFactory-middleware, vilket hjälper oss att hantera våra fel genom att använda try-catch på ett och samma ställe.
  • Därutöver har vi schemat createUserSchema, updateUserSchema och validateLogin för att validera kroppen innan vi startar nästa middleware.

Syntaxen för HTTP-metoden är:

Skapa Controller:

Skapa filen user.controller.js i mappen controllers och kopiera-klistra in detta:

Som nämnts ovan innehåller controllerfilen vår affärslogik för hantering av våra rutter.
I vårt exempel använder vissa metoder klassen UserModel för att fråga databasen för att hämta data.
För att returnera data i varje middleware använder vi res.send(result) för att skicka ett svar till klienten.

Skapa modellen:

Skapa filen user.model.js i katalogen models och kopiera och klistra in detta:

Den här klassen upprättar anslutningen mellan styrenheten och databasen.
Här har vi alla metoder som hämtar argumenten från styrenheten, gör en förfrågan, förbereder uttalanden, ansluter till databasen med hjälp av förfrågningsmetoden från db-connection-klassen, skickar förfrågan med arrayen med förberedda uttalanden och får tillbaka resultatet.
Varje funktion returnerar resultatet till styrenheten.

.gitIgnore:

Om du bestämmer dig för att lägga till det här projektet i din GitHub, glöm inte att skapa en .gitignore-fil och kopiera-klistra in den här:

node_modules.env
Enter fullscreen mode Exit fullscreen mode

Den här filen talar bara om för git vilka filer som den ska ignorera.
Du bör undvika katalogen node_modules eftersom den är tung och inte nödvändig för arkivet.
När någon klonar detta arkiv kommer de att använda kommandot “npm I” för att installera alla beroenden.
Att ignorera .env-filen är för att dölja dina privata konfigurationer för andra utvecklare som använder din kod.

Lämna ett svar

Din e-postadress kommer inte publiceras.