Cum să construim API Rest cu NodeJS, Express și MySQL

Cu cunoștințe de JavaScript și MySQL, putem construi API-ul NodeJS folosind Express.

Am făcut câteva cercetări și am încercat să dezvolt un API de la zero.
Îmi place să simplific lucrurile și încerc să evit duplicarea codului.

Acest ghid vă va arăta cum să construiți un API de la zero:
Vă veți învăța cum să creați rute,
cum să folosiți mysql2, cum să configurați și să vă conectați la baza de date și cum să executați interogări cu declarații pregătite.
Cum să creați un middleware care poate primi un argument suplimentar în afară de req, res și următorul callback.
Vă veți învăța cum să verificați datele din obiectul de cerere folosind modulul Express Validator.
Vă veți învăța cum să utilizați modulul JWT pentru a crea un token pentru utilizator, să verificați tokenul și să obțineți obiectul stocat în token.
În plus, veți învăța cum să oferiți utilizatorilor permisiunea de a accesa o anumită rută pe baza rolurilor lor de utilizator.

Tehnologii și pachete:

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

Instalarea MySQL:

Eu folosesc WSL și puteți folosi acest tutorial pentru a vedea cum se instalează MySQL în WSL.
Trebuie să vă asigurați că MySQL rulează cu această comandă:

sudo service mysql status
Enter fullscreen mode Exit fullscreen mode

Dacă nu rulează, folosiți doar:

sudo service mysql start
Enter fullscreen mode Exit fullscreen mode

Prezentare generală a aplicației:

Vom construi un API rest pentru operațiile CRUD: crearea, citirea, actualizarea și ștergerea utilizatorilor.

Creați folderul proiectului și instalați toate dependențele:

mkdir mysql-node-express && cd mysql-node-expressnpm init -ynpm i express express-validator mysql2 cors dotenv jsonwebtoken -Snpm i nodemon -D
Intrați în modul fullscreen Ieșiți din modul fullscreen

Veziți fișierul package.json și schimbați valoarea “main” în “src/server”.js” și adăugați aceste scripturi la obiectul scripturi:

"start": "node src/server.js","dev": "nodemon"
Intrați în modul fullscreen Ieșiți din modul fullscreen

package.json ar trebui să arate astfel:

Creați fișierul .env:

Vom folosi fișierul .env pentru a gestiona toate variabilele noastre de mediu.
Filetul .env este un fișier ascuns care ne permite să ne personalizăm variabilele de mediu folosind sintaxa ENV VARIABLE = VALUE.
Aceste variabile sunt încărcate cu ajutorul modulului dotenv pe care l-am instalat deja.
Fisierul .env poate fi definit în diferite stadii ale mediului (medii de dezvoltare / stadiu / producție).

Creați fișierul .env, copiați următoarele linii și actualizați fișierul cu numele db_name, db_username și parola MySQL:

# DB ConfigurationsHOST=localhostDB_USER=db_usernameDB_PASS=db_passwordDB_DATABASE=db_name# local runtime configsPORT=3000SECRET_JWT=supersecret
Intrați în modul fullscreen Ieșiți din modul fullscreen

Creați fișierul nodemon.json:

Nodemon este un instrument care ajută la dezvoltarea de aplicații bazate pe node.js prin repornirea automată a aplicației node atunci când sunt detectate modificări de fișiere în directorul țintă.
Nodemon este un înveliș de înlocuire pentru node. În loc să folosim comanda node, ar trebui să folosim comanda nodemon pe linia de comandă pentru a executa scriptul nostru.
Potem adăuga cu ușurință comutatoare de configurare în timp ce executăm nodemon pe linia de comandă, cum ar fi:

nodemon --watch src
Intră în modul fullscreen Ieșire din modul fullscreen

De asemenea, putem folosi un fișier (nodemon.json) pentru a specifica toate comutatoarele.
Dacă dorim să urmărim mai multe fișiere dintr-un director, putem adăuga directorul în matricea “watch”.
Dacă vrem să căutăm o anumită extensie (cum ar fi un fișier ts), putem folosi proprietatea “ext”.
Dacă vrem să ignorăm unele fișiere, le putem defini în matricea “ignore”‘, și așa mai departe…
Utilizez acest fișier mai ales atunci când creez un server cu NodeJS bazat pe typescript, dar cred că este mai ușor să avem mai multe locuri pentru a include configurațiile aplicațiilor noastre.
Acest fișier este opțional.

Creați fișierul nodemon.json și adăugați acest lucru la fișier:

{ "watch": , "ext": ".js", "ignore": }
Intră în modul fullscreen Ieșiți din modul fullscreen

Creați folderul src:

mkdir src && cd src
Intrați în modul fullscreen Ieșiți din modul fullscreen

În folderul src creați subfolderele: controllers, models, routes, middleware, db și utils:

mkdir controllers models routes middleware db utils
Intrați în modul fullscreen Ieșiți din modul fullscreen

Configurați serverul Express:

În directorul src creați fișierul server.js și copiați aceste linii:

În acest fișier, importăm express pentru a construi API-urile rest și folosim express.json() pentru a analiza cererile primite cu sarcini utile JSON.

De asemenea, importăm modulul dotenv pentru a citi fișierul .env config file pentru a obține numărul de port pentru a rula serverul.

De asemenea, importăm și userRouter.

După asta, avem un middleware care gestionează erorile 404 → dacă cineva caută un endpoint care nu există, va primi această eroare: “Endpoint Not Found” cu codul de stare 404. După aceea, folosim un middleware de eroare care va obține datele de eroare din rutele anterioare. dacă este apelat next(err), puteți vedea middleware-ul 404 ca exemplu.
Ascultăm portul din fișierul.env și imprimăm în consolă că serverul rulează.

Creăm baza de date MySQL și tabelul de utilizatori:

În directorul db, vom crea fișierul create-user-db.sql și vom face copy-paste la aceste linii:

În acest script, mai întâi renunțăm la baza de date dacă există pentru a putea fi resetată rapid în caz de greșeală (puteți comenta această linie dacă doriți), apoi, creăm baza de date dacă nu există. O stabilim ca bază de date activă și creăm un tabel “user” cu toate coloanele (id, nume de utilizator și așa mai departe), permițând din nou o resetare convenabilă în caz de nevoie. Puteți rula această interogare în clientul bazei de date, dacă folosiți unul.

Dacă folosiți wsl, în directorul db puteți rula:

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

Configure and Connect to MySQL database:

Crearea unui fișier suplimentar în directorul db numit db-connection.js și copy-paste acest lucru:

În acest fișier, mai întâi importăm modulul dotenv și îl folosim pentru a citi informațiile de configurare a bazei de date, cum ar fi db host, db user din fișierul.env.

Verificăm conexiunea în cazul în care există o problemă cu baza de date și apoi eliberăm conexiunea.

Avem o metodă de interogare care returnează o promisiune a rezultatului interogării.

Utilizăm un bloc try-catch pentru a capta erorile comune MySQL și a returna codurile de stare și mesajele HTTP corespunzătoare.

La sfârșitul fișierului, creăm o instanță a clasei DBConnection și folosim metoda de interogare, iar în model.js (pe care îl vom vedea în pasul următor), vom folosi din nou metoda de interogare.

Creăm Error Handler:

În continuare, vom crea gestionarul nostru de erori.

Pentru a face acest lucru, mai întâi, vom crea fișierul HttpException.utils.js în directorul utils și vom face copy-paste după cum urmează:

Clasa HttpException moștenește clasa Error.
Constructorul va obține starea, mesajul și datele. Vom trece variabila mesaj către constructorul părinte folosind super(mesaj), iar apoi vom inițializa variabilele de instanță status, mesaj și date.

După aceea, vom crea un gestionar de erori middleware în directorul middleware.
Vom crea o eroare. middleware.js și vom face un copy-paste cu următoarele:

Puteți vedea în partea de jos a fișierului cum va fi obiectul.

Middleware-ul va primi req, res și callback-ul next, dar va primi și un argument suplimentar, error (prin utilizarea next(error) înainte de a ajunge la acest middleware).

Utilizăm destructurarea pentru a obține variabilele din obiectul error și setăm statusul la 500 dacă nu a fost configurat înainte.

După aceasta, indiferent dacă statusul este 500, vom avea grijă să modificăm mesajul astfel încât utilizatorul să primească un mesaj generic de eroare internă a serverului, fără a dezvălui natura exactă a eșecului.

După aceasta, creăm un obiect de eroare cu proprietățile tip, status și mesaj (datele sunt opționale).

Creăm fișiere utils (helpers):

În directorul utils, creăm încă două fișiere, common.utils.js și userRoles.utils.js.

common.utils.js:

Această funcție ajută la setarea mai multor câmpuri pentru interogări pregătite cu perechi cheie-valoare.
ColumnSet matricea de perechi cheie =?,
Valorile ar trebui, prin urmare, să fie în aceeași ordine ca și matricea columnSet.

userRoles.utils.js:

module.exports = { Admin: 'Admin', SuperUser: 'SuperUser'}
Intrați în modul ecran complet Ieșiți din modul ecran complet

Creați funcția Async:

Creați un alt fișier numit awaitHandlerFactory.middleware.js în directorul middleware și copiați și lipiți acest lucru:

În general, știm că middleware-ul este doar o metodă asincronă care primește argumentele req, res și next, așa că, dacă vrem ca acest middleware să primească un argument suplimentar, o vom face în acest mod (vom folosi acest lucru și în middleware-ul auth în pasul următor).

Această funcție va obține un callback, va rula scriptul middleware-ului și va încerca să declanșeze acest callback în blocul try.
Dacă ceva nu merge bine aici, va prinde eroarea și vom folosi next(err) (care o va transfera la următorul middleware => error.middleware.js).

Creați middleware-ul de autentificare:

Un alt middleware de care avem nevoie este middleware-ul auth pe care îl vom folosi pentru a verifica permisiunile utilizatorilor prin intermediul modulului JWT.

Similar cu middleware-ul awaitHandlerFactory.middleware.js, avem aici un middleware care necesită un argument suplimentar (care este opțional) => roles.

Am folosit try-catch pentru a ajusta statusul de eroare în zona de captură la 401 (dacă token-ul a expirat, de exemplu).

La început, căutăm req.headers.authorization – dacă nu este definit în antet sau dacă antetul nu începe cu “Bearer “, utilizatorul va primi un răspuns 401. Dacă începe cu “Bearer “, vom obține token-ul și vom folosi cheia secretă din fișierul.env pentru a-l descifra.

Vom verifica token-ul folosind funcția sincronă jwt.verify, care primește token-ul și secretKey ca argumente și returnează sarcina utilă decodificată, dacă semnătura este validă și dacă câmpurile opționale expiration, audience sau issuer sunt valide. În caz contrar, va arunca o eroare.

Acum, putem găsi utilizatorul cu acest token căutând ID-ul utilizatorului.
Dacă utilizatorul nu mai există, va primi o excepție de 401 fără nicio informație.
Dacă utilizatorul există, vom verifica dacă utilizatorul actual este proprietarul care își caută rutele sau dacă utilizatorul are rolul de a accesa această rută.
Salvăm utilizatorul actual doar în cazul în care dorește să obțină datele sale pe următorul middleware (cum ar fi ruta “whoami”).

Validarea datelor folosind modulul Express Validator:

În directorul middleware, vom crea un fișier suplimentar pe care îl vom folosi pentru a verifica proprietățile req.body.

Creăm un subfolder în directorul middleware numit validators și creăm un fișier în acest director, userValidator.middleware.js. Copiați și lipiți acest lucru:

În acest fișier, am folosit modulul express-validator, care este foarte ușor de utilizat ori de câte ori avem nevoie să verificăm unele proprietăți, să verificăm dacă proprietatea există sau să creăm verificări personalizate cu un mesaj personalizat pentru utilizator în cazul în care orice valoare a unei proprietăți nu este validă.

Acum putem începe să creăm fișierele noastre de rute, controler și model.

Definiți rutele:

Crearea fișierului user.route.js în directorul routes și copy-paste acest lucru:

Exemplul de mai sus arată cum se definesc rutele. Să încercăm să îl împărțim în bucăți:

  • Puteți crea un router folosind express.Router().Fiecare rută poate încărca o funcție middleware care se ocupă de logica de afaceri.UserController, de exemplu, poartă toate middleware-urile principale.Pentru a utiliza routerul, routerul trebuie exportat ca un modul și utilizat în aplicația principală folosind app.use(router_module).
  • Am utilizat middleware-ul auth pentru autentificarea și autorizarea utilizatorilor, pentru verificarea token-ului utilizatorului sau a rolului utilizatorului pentru traseu.În exemplul nostru, unele dintre rute utilizează middleware-ul auth pentru verificarea autentificării și autorizării utilizatorului. acest middleware va fi declanșat înainte de middleware-ul principal (cel care deține logica de afaceri). callback-ul următor trebuie să fie apelat pentru a trece controlul la următoarea metodă de middleware. în caz contrar, cererea va fi lăsată în așteptare.
  • awaitHandlerFactory (middleware try-catch) este utilizat pentru a îngloba toate middleware-urile asincrone. În acest fel, dacă unul dintre middleware-uri aruncă o eroare, awaitHandlerFactory va prinde acea eroare.Puteți vedea că toate funcțiile noastre middleware sunt înfășurate cu middleware-ul awaitHandlerFactory, ceea ce ne ajută să ne gestionăm erorile prin utilizarea try-catch într-un singur loc.
  • În plus, avem schema createUserSchema, updateUserSchema și validateLogin pentru a valida corpul înainte de a începe următorul middleware.

Sintaxa metodei HTTP este:

Creați controlerul:

Crearea fișierului user.controller.js în directorul controlorilor și copiați și lipiți acest lucru:

După cum am menționat mai sus, fișierul controller conține logica noastră de afaceri pentru gestionarea rutelor noastre.
În exemplul nostru, unele metode folosesc clasa UserModel pentru a interoga baza de date pentru a obține datele.
Pentru a returna datele în fiecare middleware, folosim res.send(result) pentru a trimite un răspuns către client.

Crearea modelului:

Și creați fișierul user.model.js în directorul models și copiați-lipiți acest lucru:

Această clasă realizează conexiunea dintre controler și baza de date.
Aici avem toate metodele care primesc argumentele de la controler, fac o interogare, pregătesc declarațiile, se conectează la baza de date folosind metoda de interogare din clasa db-connection, trimit cererea cu matricea de declarații pregătite și primesc rezultatul înapoi.
Care funcție returnează rezultatul către controler.

.gitIgnore:

În cazul în care vă decideți să adăugați acest proiect la GitHub, nu uitați să creați un fișier .gitignore și să copiați-lipiți acest lucru:

node_modules.env
Intră în modul ecran complet Ieși din modul ecran complet

Acest fișier doar îi spune lui git ce fișiere trebuie să ignore.
Ar trebui să evitați directorul node_modules deoarece este greu și nu este necesar pentru depozit.
Când cineva clonează acest depozit, va folosi comanda “npm I” pentru a instala toate dependențele.
Ignorarea fișierului .env este pentru a vă ascunde configurațiile private de alți dezvoltatori care vă folosesc codul.

Lasă un răspuns

Adresa ta de email nu va fi publicată.