How To Build Rest API With NodeJS, Express, and MySQL

Mając wiedzę na temat JavaScript i MySQL, możemy zbudować nasze API NodeJS używając Express.

Przeprowadziłem kilka badań i próbowałem stworzyć API od podstaw.
Lubię upraszczać rzeczy i staram się unikać powielania kodu.

Ten przewodnik pokaże ci jak zbudować API od podstaw:
Dowiesz się jak tworzyć trasy,
jak używać mysql2, jak skonfigurować i połączyć się z bazą danych, oraz jak uruchamiać zapytania z przygotowanymi instrukcjami.
Jak stworzyć middleware, który może otrzymać dodatkowy argument oprócz req, res, i next callback.
Dowiesz się jak sprawdzić dane z obiektu żądania używając modułu Express Validator.
Dowiesz się, jak używać modułu JWT do tworzenia tokena dla użytkownika, weryfikowania tokena i uzyskiwania obiektu przechowywanego w tokenie.
Dodatkowo dowiesz się, jak zapewnić użytkownikom uprawnienia do dostępu do określonej trasy na podstawie ich ról użytkownika.

Technologie i pakiety:

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

Instalacja MySQL:

Używam WSL, i możesz użyć tego samouczka, aby zobaczyć, jak zainstalować MySQL w WSL.
Musisz upewnić się, że MySQL jest uruchomiony za pomocą tego polecenia:

sudo service mysql status
Enter fullscreen mode Exit fullscreen mode

Jeśli nie jest uruchomiony, po prostu użyj:

sudo service mysql start
Enter fullscreen mode Exit fullscreen mode

Przegląd aplikacji:

Zbudujemy rest API dla operacji CRUD: tworzenia, odczytu, aktualizacji i usuwania użytkowników.

Utwórz folder projektu i zainstaluj wszystkie zależności:

mkdir mysql-node-express && cd mysql-node-expressnpm init -ynpm i express express-validator mysql2 cors dotenv jsonwebtoken -Snpm i nodemon -D
Enter fullscreen mode Exit fullscreen mode

Przejdź do pliku package.json i zmień wartość “main” na “src/server.js” i dodaj te skrypty do obiektu scripts:

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

package.json powinien wyglądać tak:

Utwórz plik .env:

Będziemy używać pliku .env do zarządzania wszystkimi naszymi zmiennymi środowiskowymi.
Plik .env jest ukrytym plikiem, który pozwala nam dostosować nasze zmienne środowiskowe za pomocą składni ENV VARIABLE = VALUE.
Zmienne te są ładowane za pomocą modułu dotenv, który już zainstalowaliśmy.
Plik .env może być zdefiniowany na różnych etapach środowiska (develop / stage / production environments).

Utwórz plik .env, skopiuj poniższe linie i zaktualizuj plik o swoją nazwę db_name MySQL, db_username i hasło:

# DB ConfigurationsHOST=localhostDB_USER=db_usernameDB_PASS=db_passwordDB_DATABASE=db_name# local runtime configsPORT=3000SECRET_JWT=supersecret
Wejdź w tryb pełnoekranowy Wyjdź z trybu pełnoekranowego

Utwórz plik nodemon.json:

Nodemon jest narzędziem, które pomaga w tworzeniu aplikacji opartych na node.js poprzez automatyczne restartowanie aplikacji node po wykryciu zmian plików w katalogu docelowym.
Nodemon jest zastępczym opakowaniem dla node. Zamiast używać polecenia node, powinniśmy użyć polecenia nodemon w wierszu poleceń, aby wykonać nasz skrypt.
Możemy łatwo dodać przełączniki konfiguracyjne podczas uruchamiania nodemon w wierszu poleceń, takie jak:

nodemon --watch src
Enter fullscreen mode Exit fullscreen mode

Możemy również użyć pliku (nodemon.json), aby określić wszystkie przełączniki.
Jeśli chcemy oglądać kilka plików w katalogu, możemy dodać katalog w tablicy “watch”.
Jeśli chcemy wyszukać konkretne rozszerzenie (takie jak plik ts) możemy użyć właściwości “ext”.
Jeśli chcemy zignorować niektóre pliki, możemy je zdefiniować w tablicy “ignore”‘, i tak dalej…
Używam tego pliku głównie gdy tworzę serwer z NodeJS oparty na typescript, ale myślę, że łatwiej jest mieć więcej miejsc do zawarcia konfiguracji naszej aplikacji.
Ten plik jest opcjonalny.

Utwórz plik nodemon.json i dodaj to do pliku:

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

Utwórz folder src:

mkdir src && cd src
Enter fullscreen mode Exit fullscreen mode

W folderze src utwórz podfoldery: controllers, models, routes, middleware, db, and utils:

mkdir controllers models routes middleware db utils
Wejdź w tryb fullscreen Wyjdź z trybu fullscreen

Skonfiguruj serwer Express:

W katalogu src utwórz plik server.js i skopiuj te linie:

W tym pliku importujemy express, aby zbudować pozostałe API i użyć express.json() do parsowania przychodzących żądań z ładunkiem JSON.

Importujemy również moduł dotenv, aby odczytać plik .env, aby uzyskać numer portu do uruchomienia serwera.

Importujemy również userRouter.

Po tym mamy middleware, który obsługuje błędy 404 → jeśli ktoś szuka punktu końcowego, który nie istnieje, dostanie ten błąd: ‘Endpoint Not Found’ z kodem statusu 404. Po tym, używamy error middleware, który będzie pobierał dane o błędach z poprzednich tras. jeśli wywołana zostanie next(err), możesz zobaczyć 404 middleware jako przykład.
Słuchamy na porcie z pliku.env i wypisujemy do konsoli, że serwer jest uruchomiony.

Tworzymy bazę danych MySQL i tabelę użytkowników:

W katalogu db utworzymy plik create-user-db.sql i skopiuj-wklej te linie:

W tym skrypcie najpierw upuszczamy bazę danych, jeśli istnieje, aby można ją było szybko zresetować w razie błędu (możesz tę linię skomentować, jeśli chcesz), następnie, tworzymy bazę danych, jeśli nie istnieje. Ustawiamy ją jako naszą aktywną bazę danych i tworzymy tabelę “user” ze wszystkimi kolumnami (id, nazwa użytkownika, i tak dalej), ponownie pozwalając na wygodny reset w razie potrzeby. Możesz uruchomić to zapytanie w swoim kliencie bazy danych, jeśli go używasz.

Jeśli używasz wsl, w katalogu db możesz uruchomić:

mysql -u -p < create-user-db.sql
Wejdź w tryb pełnoekranowy Wyjdź z trybu pełnoekranowego

Konfiguracja i połączenie z bazą danych MySQL:

Utwórz dodatkowy plik w katalogu db nazywa się db-connection.js, i skopiuj-wklej to:

W tym pliku najpierw importujemy moduł dotenv i używamy do odczytania informacji o konfiguracji bazy danych jak db host, db user z pliku.env.

Sprawdzamy połączenie w przypadku wystąpienia problemu z bazą danych, a następnie zwalniamy połączenie.

Posiadamy metodę query, która zwraca obietnicę wyniku zapytania.

Używamy bloku try-catch do przechwytywania typowych błędów MySQL i zwracania odpowiednich kodów statusu HTTP i komunikatów.

Na końcu pliku tworzymy instancję klasy DBConnection i używamy metody query, a w pliku model.js (który zobaczymy w następnym kroku) ponownie użyjemy metody query.

Tworzymy Error Handler:

Następnie stworzymy nasz error handler.

W tym celu najpierw stworzymy plik HttpException.utils.js w katalogu utils, a następnie skopiuj-wklej następujące elementy:

Klasa HttpException dziedziczy po klasie Error.
Konstruktor pobierze status, komunikat oraz dane. Przekażemy zmienną message do konstruktora nadrzędnego za pomocą super(message), a następnie zainicjalizujemy zmienne instancyjne status, message i data.

Potem utworzymy w katalogu middleware obsługę błędów.
Utworzymy plik error. middleware.js i skopiuj-wklej następujące elementy:

Na dole pliku widzimy jak będzie wyglądał obiekt.

Program middleware dostanie req, res, i next callback, ale dostanie też dodatkowy argument, error (poprzez użycie next(error) zanim dojdziemy do tego middleware).

Użyjemy destructuring aby uzyskać zmienne z obiektu błędu i ustawimy status na 500 jeśli nie został on wcześniej skonfigurowany.

Po tym, niezależnie od tego czy status jest 500, upewnimy się, że zmienimy komunikat tak, aby użytkownik otrzymał ogólny komunikat o błędzie wewnętrznym serwera bez ujawniania dokładnej natury niepowodzenia.

Po tym, tworzymy obiekt błędu z właściwościami type, status, i message (dane są opcjonalne).

Tworzymy pliki utils (pomocnicze):

W katalogu utils tworzymy jeszcze dwa pliki, common.utils.js, oraz userRoles.utils.js.

common.utils.js:

Ta funkcja pomaga ustawić wiele pól dla przygotowanych zapytań z parami klucz-wartość.
ColumnSet tablicę par klucz =?,
Wartości powinny być zatem w tej samej kolejności co tablica columnSet.

userRoles.utils.js:

module.exports = { Admin: 'Admin', SuperUser: 'SuperUser'}
Enter fullscreen mode Exit fullscreen mode

Create Async function:

Utwórz kolejny plik o nazwie awaitHandlerFactory.middleware.js w katalogu middleware i skopiuj-wklej to:

Ogólnie wiemy, że middleware jest tylko metodą asynchroniczną, która dostaje argumenty req, res i next, więc, jeśli chcemy, aby ten middleware dostał dodatkowy argument, zrobimy to w ten sposób (użyjemy tego również w auth middleware w następnym kroku).

Ta funkcja dostanie callback, uruchomi skrypt middleware i będzie próbowała wywołać ten callback w bloku try.
Jeśli coś pójdzie nie tak, złapie błąd i użyjemy next(err) (który przekaże go do następnego middleware => error.middleware.js).

Create Authentication Middleware:

Kolejnym middleware, którego potrzebujemy, jest auth middleware, którego będziemy używać do sprawdzania uprawnień użytkowników poprzez moduł JWT.

Podobnie jak w przypadku awaitHandlerFactory.middleware.js middleware, mamy tutaj middleware, który wymaga dodatkowego argumentu (który jest opcjonalny) => role.

Użyłem try-catch, aby dostosować status błędu w obszarze catch do 401 (jeśli token wygasł, na przykład).

Na początku szukamy req.headers.authorization – jeśli nie jest zdefiniowany w nagłówku lub jeśli nagłówek nie zaczyna się od “Bearer “, użytkownik otrzyma odpowiedź 401. Jeśli zaczyna się od “Bearer “, pobierzemy token i użyjemy tajnego klucza z pliku.env do jego odszyfrowania.

Weryfikujemy token używając synchronicznej funkcji jwt.verify, która pobiera token i secretKey jako argumenty i zwraca zdekodowany payload, czy podpis jest ważny i czy opcjonalne pola expiration, audience i issuer są ważne. W przeciwnym razie wyrzuci błąd.

Teraz, możemy znaleźć użytkownika z tym tokenem poprzez przeszukiwanie id użytkownika.
Jeśli użytkownik już nie istnieje, otrzyma wyjątek 401 bez żadnych informacji.
Jeśli użytkownik istnieje, sprawdzimy czy aktualny użytkownik jest właścicielem, który szuka swoich tras lub czy użytkownik ma rolę dostępu do tej trasy.
Zapisujemy aktualnego użytkownika na wszelki wypadek, gdyby chciał uzyskać swoje dane na kolejnym middleware (jak trasa “whoami”).

Walidacja danych przy użyciu modułu Express Validator:

W katalogu middleware utworzymy dodatkowy plik, który wykorzystamy do weryfikacji właściwości req.body.

W katalogu middleware utwórz podkatalog o nazwie validators i utwórz w tym katalogu plik userValidator.middleware.js. Kopiuj-wklej to:

W tym pliku użyłem modułu express-validator, który jest bardzo łatwy w użyciu, gdy potrzebujemy sprawdzić niektóre właściwości, sprawdzić, czy właściwość istnieje, lub stworzyć niestandardowe kontrole z niestandardowym komunikatem dla użytkownika, jeśli jakaś wartość właściwości nie jest ważna.

Teraz możemy zacząć tworzyć nasze pliki tras, kontrolerów i modeli.

Zdefiniuj trasy:

Utwórz plik user.route.js w katalogu routes i skopiuj-wklej to:

Powyższy przykład pokazuje jak definiować trasy. Spróbujmy rozłożyć to na części:

  • Możesz utworzyć router za pomocą express.Router().Każda trasa może załadować funkcję middleware, która obsługuje logikę biznesową.UserController, na przykład przenosi wszystkie główne middlewares.Aby użyć routera, router powinien być wyeksportowany jako moduł i użyty w głównej aplikacji za pomocą app.use(router_module).
  • Użyliśmy auth middleware do uwierzytelniania i autoryzacji użytkownika, do sprawdzania tokena użytkownika lub roli użytkownika dla trasy.W naszym przykładzie niektóre trasy używają oprogramowania pośredniego auth do sprawdzania uwierzytelniania i autoryzacji użytkownika.To oprogramowanie pośrednie zostanie uruchomione przed głównym oprogramowaniem pośrednim (tym, które przechowuje logikę biznesową).Następny callback musi zostać wywołany, aby przekazać kontrolę do następnej metody oprogramowania pośredniego.W przeciwnym razie żądanie zostanie pozostawione zawieszone.
  • awaitHandlerFactory (oprogramowanie pośrednie try-catch) jest używane do zawijania całego asynchronicznego oprogramowania pośredniego. W ten sposób, jeśli jeden z middleware rzuci błąd, awaitHandlerFactory złapie ten błąd.Możesz zobaczyć, że wszystkie nasze funkcje middleware są owinięte z awaitHandlerFactory middleware, co pomaga nam obsługiwać nasze błędy za pomocą try-catch w jednym miejscu.
  • Dodatkowo mamy schemat createUserSchema, updateUserSchema i validateLogin do walidacji ciała przed uruchomieniem kolejnego middleware.

Składnia metody HTTP to:

Utwórz kontroler:

Utwórz plik user.controller.js w katalogu controllers i skopiuj-wklej to:

Jak wspomniano powyżej, plik kontrolera przechowuje naszą logikę biznesową do obsługi naszych tras.
W naszym przykładzie, niektóre metody używają klasy UserModel do odpytywania bazy danych w celu uzyskania danych.
Aby zwrócić dane w każdym oprogramowaniu pośredniczącym, używamy res.send(result) do wysłania odpowiedzi do klienta.

Tworzenie modelu:

I utwórz plik user.model.js w katalogu models i skopiuj-wklej to:

Ta klasa tworzy połączenie między kontrolerem a bazą danych.
Mamy tu wszystkie metody, które pobierają argumenty z kontrolera, wykonują zapytanie, przygotowują deklaracje, łączą się z bazą danych za pomocą metody query z klasy db-connection, wysyłają zapytanie z przygotowaną tablicą deklaracji i otrzymują wynik z powrotem.
Każda funkcja zwraca wynik do kontrolera.

.gitIgnore:

W przypadku, gdy zdecydujesz się dodać ten projekt do swojego GitHuba, nie zapomnij utworzyć pliku .gitignore i skopiuj-wklej to:

node_modules.env
Enter fullscreen mode Exit fullscreen mode

Ten plik po prostu mówi gitowi, które pliki powinien ignorować.
Powinieneś unikać katalogu node_modules, ponieważ jest on ciężki i nie jest niezbędny dla repozytorium.
Kiedy ktoś sklonuje to repozytorium, użyje polecenia “npm I”, aby zainstalować wszystkie zależności.
Ignorowanie pliku .env ma na celu ukrycie twoich prywatnych konfiguracji przed innymi programistami używającymi twojego kodu.

.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.