Создайте приложение NestJS для аутентификации с помощью Postgres, JWT и переменных среды.

Иногда NestJS может стать ошеломляющим для новичка. Я знаю это, потому что когда-то я тоже был в этой точке. Я имею в виду, что для того, чтобы написать API с аутентификацией, нам приходится иметь дело со многими вещами: самим NestJS, TypeORM, базой данных, JWT, паспортами, псевдонимами путей, DTO, проверкой, защитой, каналами и так далее.

Но позвольте мне сказать вам, это не так сложно, как кажется!

Требования

Требуется иметь базовое представление о Node.js и PostgreSQL, которые вы установили локально на свой компьютер. Я выберу Visual Studio Code в качестве редактора кода. Вы можете использовать все, что вы предпочитаете.

База данных

Во-первых, нам нужно создать нашу базу данных PostgreSQL. Я знаю, все решают это по-своему, некоторые люди используют графический интерфейс, но мы собираемся использовать наш терминал. Опять же, на вашем компьютере должен быть установлен PostgreSQL. Если у вас установлен PostgreSQL, следующие четыре команды будут выполняться на компьютерах с Linux, Mac и Windows.

$ psql postgres
$ CREATE DATABASE nest_auth_api;
$ \l
$ \q

Объяснение команды:

  • psql postgres открывает интерфейс командной строки psql с пользователем postgres
  • CREATE DATABASE nest_auth_api; создаем нужную нам базу данных
  • \l перечислить все базы данных
  • \q выйти из интерфейса командной строки psql

Мой терминал будет выглядеть так после того, как мы успешно выполним все четыре команды. Как мы видим, база данных nest_auth_api создана.

Приложение NestJS

Давайте продолжим с NestJS. Мы собираемся установить NestJS CLI, поэтому откройте терминал по вашему выбору и введите:

$ npm i -g @nestjs/cli

Мы инициализируем новый проект NestJS с его CLI. Это может занять до минуты.

$ nest new nest-auth-api -p npm

После выполнения этой команды вы можете открыть свой проект в редакторе кода. Поскольку я использую код Visual Studio, я собираюсь открыть проект, набрав:

$ cd nest-auth-api
$ code .

Мой проект выглядит так в VSCode (код Visual Studio):

Следующий шаг является необязательным, но обычно я фиксирую первоначальную инициализацию, поэтому набираю в терминале:

$ git add .
$ git commit -m "chore(): init nest.js"

Установка зависимостей

Давайте установим некоторые зависимости, которые нам понадобятся.

$ npm i @nestjs/config @nestjs/typeorm @nestjs/jwt @nestjs/passport passport-jwt typeorm pg passport class-transformer class-validator class-sanitizer bcryptjs
$ npm i -D @types/passport-jwt @types/node

Создать структуру проекта

Чтобы иметь чистую структуру проекта, мы создадим несколько папок и файлов. Для различных файлов, таких как файлы среды или общие вспомогательные файлы, я обычно создаю новую папку с именем common.

$ mkdir src/shared && mkdir src/shared/typeorm
$ mkdir src/common && mkdir src/common/envs && mkdir src/common/helper

Кроме того, давайте создадим несколько общих файлов.

$ touch src/shared/typeorm/typeorm.service.ts
$ touch src/common/envs/development.env
$ touch src/common/helper/env.helper.ts

Теперь нам также нужно создать наши модули. Для простоты мы просто используем модули Пользователь и Аутентификация.

$ nest g mo api
$ nest g mo api/user && nest g co api/user --no-spec && nest g s api/user --no-spec
$ nest g mo api/user/auth && nest g co api/user/auth --no-spec && nest g s api/user/auth --no-spec
$ touch src/api/user/user.dto.ts
$ touch src/api/user/user.entity.ts
$ touch src/api/user/auth/auth.dto.ts
$ touch src/api/user/auth/auth.guard.ts
$ touch src/api/user/auth/auth.helper.ts
$ touch src/api/user/auth/auth.strategy.ts

Дерево нашего проекта сейчас должно выглядеть так:

Как видите, у нас много дел.

Переменные среды

Чтобы иметь файлы среды в нашей папке dist/ всякий раз, когда мы собираем наше приложение, нам нужно добавить правило компилятора в нашу папку nest-cli.json для сопоставления файлов нашей среды.

Поэтому, пожалуйста, измените nest-cli.json с

to

Теперь давайте изменим наш файл src/common/helper/env.helper.ts, чтобы загрузить наш файл среды, когда мы позже импортируем наш ConfigModule.

Эта функция возвращает абсолютный путь к нашему файлу среды на основе нашего файла NODE_ENV. У него в основном есть два запасных варианта. Во-первых, если NODE_ENV не задано, мы пытаемся получить абсолютный путь к файлу development.env. Давайте продолжим оживлять наш файл окружения.

Вы можете увидеть путь к файлу под каждым фрагментом кода.

Псевдонимы путей

Чтобы иметь чистые пути к файлам, мы собираемся изменить наш tsconfig.json в нашем корневом каталоге. Если вы не знаете, что такое псевдоним пути, прочитайте мою статью Простой способ использования псевдонимов пути в NestJS.

Вкратце: он превращает пути к файлам из-за импорта из чего-то вроде ../../../folder/file.ts в @/folder/file.ts.

Теперь давайте изменим tsconfig.json с:

to

Настройка и подключение к базе данных (Postgres)

Нам нужно подключить наше приложение NestJS к базе данных Postgres, которую мы создали ранее. Помните, мы создали базу данных и файл среды, в котором мы храним важную информацию о базе данных. Итак, мы должны объединить все это в два этапа.

  1. Мы создаем сервис конфигурации TypeORM.
  2. Мы загружаем этот сервис и ConfigModule в наш основной модуль.

Давайте добавим некоторый контент в наш файл src/shared/typeorm/typeorm.service.ts:

Теперь нам нужно изменить src/app.module.ts с:

to

Большой! Теперь давайте добавим дополнительную конфигурацию в наш файл main.ts, давайте изменим ее с:

to

Самое важное, что мы сделали здесь, — это строка 12. Мы добавили глобальную валидацию pipe, чтобы потом проверить тело запроса.

Сейчас мы уже много чего сделали. Мы создали файлы среды, службу для подключения к нашей базе данных и множество других конфигураций, чтобы подготовить наше приложение NestJS к тому, что будет дальше.

В любом случае, пришло время протестировать наш код, запустив:

$ npm run start:dev

Вывод в нашем терминале должен выглядеть так:

Дайте мне знать в комментариях, если вы столкнулись с ошибками. Если все работает, вы можете остановить это приложение, нажав CTRL+C.

Пользовательская сущность

Прежде чем мы начнем с модуля аутентификации, нам нужно добавить нашу пользовательскую сущность, поскольку модуль аутентификации полагается на это.

Давайте добавим содержимое в наш файл src/api/user/user.entity.ts:

Поскольку мы включили синхронизацию в нашем сервисе TypeORM, TypeORM автоматически создаст таблицу на основе этого объекта.

Что такое@Exclude()?

Вы могли видеть этот декоратор в строке 12. По сути, это означает, что мы собираемся удалить это свойство (пароль) из наших данных ответа. Подробнее об этом можно прочитать здесь.

Аутентификация

Это наш самый важный модуль, здесь магия статистики.

Класс AuthHelper

Этот класс очень важен, так как у нас есть несколько помощников по шифрованию и проверке. Ознакомьтесь с комментариями над методами, чтобы увидеть, что делает каждый метод.

Давайте добавим немного кода в src/api/user/auth/auth.helper.ts

Сторожить

Этот класс также будет вызываться всякий раз, когда нам потребуется аутентификация на наших конфиденциальных конечных точках.

Давайте добавим немного кода в src/api/user/auth/auth.guard.ts

Стратегия JWT

Теперь мы можем выполнить наше последнее требование: защитить конечные точки, требуя наличия действительного JWT в запросе. Паспорт может помочь нам и здесь. Он обеспечивает стратегию паспорта-jwt для защиты наших конечных точек позже.

Давайте добавим немного кода в src/api/user/auth/auth.strategy.ts

Теперь мы закончили реализацию стратегии JWT. Давайте продолжим реализацию нашей конечной точки.

Служба аутентификации

Если вы знакомы с NestJS, вы знаете, о чем идет речь. Вот бизнес-логика наших конечных точек. Методы здесь вызываются из нашего контроллера.

Примечание. Если это приложение запущено, эта модификация нарушит вашу сборку, поскольку мы собираемся внедрить репозиторий, который мы еще не импортировали. Чтобы не было путаницы, лучше завершить процесс в нашем терминале по CTRL+C.

Давайте изменим src/api/user/auth/auth.service.ts с:

to

Проверка

Помните, ранее в этом руководстве мы добавили глобальный канал для проверки в наш файл main.ts. Эта конфигурация в сочетании с объектами передачи данных поможет нам проверять и преобразовывать тело входящих HTTP-запросов.

Давайте добавим немного кода в src/api/user/auth/auth.dto.ts

Контроллер авторизации

Мы почти закончили с модулем аутентификации, осталось всего два шага.

Теперь нам нужно добавить наши конечные точки для регистрации, входа и обновления нашего токена JWT. Имейте в виду, что мы делаем это здесь просто, мы также могли бы взорвать эти конечные точки, но мы не делаем этого для простоты, так как я знакомлю вас с основами.

Давайте изменим файл src/api/user/auth/auth.controller.ts с:

to

Модуль аутентификации

И последнее, но не менее важное: нам нужно импортировать все модули, провайдеры и контроллеры, которые мы только что написали.

нам нужно изменить наш src/api/user/auth/auth.module.ts с:

to

Пользователь

Сейчас мы почти закончили этот проект. Нам просто нужно добавить немного сахара для демонстрации, считайте этот шаг необязательным, потому что на самом деле наш API аутентификации NestJS уже работает, в любом случае, давайте закончим это очень быстро. Все, что мы собираемся сделать здесь, это создать конечную точку для изменения имени пользователя.

Имейте в виду, что мы уже добавили объект пользователя ранее в этом руководстве.

Проверка

Нам нужна простая проверка для нашей конечной точки, поэтому давайте добавим код в src/api/user/user.dto.ts.

Услуга

Мы собираемся создать небольшой метод, который позволит нам изменить имя пользователя. Это очень просто.

Примечание. Если это приложение запущено, эта модификация нарушит вашу сборку, поскольку мы собираемся внедрить репозиторий, который мы еще не импортировали. Чтобы не было путаницы, лучше завершить процесс в нашем терминале по CTRL+C.

Давайте изменим файл src/api/user/user.service.ts с:

to

Контроллер

Здесь мы собираемся создать нашу конечную точку. Мы собираемся снова разместить JwtAuthGuard, что означает, что только запросы с действительным токеном JWT могут быть успешными.

Итак, давайте изменим файл src/api/user/user.controller.ts с:

to

Модуль

Добро пожаловать на самый последний шаг в этом руководстве, который очень прост, все, что нам нужно сделать, это импортировать объект пользователя.

Давайте изменим файл src/api/user/user.module.ts с:

to

Вау, какое путешествие! Хотите верьте, хотите нет, но мы, наконец, закончили наш проект. Теперь давайте запустим наше приложение, выполнив:

$ npm run start:dev

Ваш терминал должен дать такой ответ:

Тестирование наших конечных точек

Теперь давайте проверим наши конечные точки. Вы можете использовать такие программы, как Postman или просто с помощью команд CURL в вашем терминале.

Зарегистрироваться

$ curl -X POST http://localhost:3000/auth/register -H "Content-Type: application/json" -d '{"name": "Elon Musk", "email": "[email protected]", "password": "12345678"}'
Server Response:
{"name": "Elon Musk", "email": "[email protected]", "lastLoginAt": null, "id": 1}

Если вы снова запустите эту команду, вы столкнетесь с ошибкой 409 (конфликт), потому что этот пользователь с таким адресом электронной почты уже существует.

Если вы выберете пароль длиной менее 8 символов, вы получите ошибку 400 (неверный запрос), поскольку нам требуется пароль с минимальной длиной 8 символов.

Авторизоваться

$ curl -X POST http://localhost:3000/auth/login -H "Content-Type: application/json" -d '{"email": "[email protected]", "password": "12345678"}'
Server Response:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NSwiZW1haWwiOiJlbG9uQGdtYWlsLmNvbSIsImlhdCI6MTY0NjU5NzY2NiwiZXhwIjoxNjc4MTMzNjY2fQ.fBG9QJKOUIvrB1iaDDs-2gJKH_qephHNRtJyiKG-aVk

Если вы выберете неправильный адрес электронной почты или пароль, вы столкнетесь с ошибкой 404 (не найдено), которую мы будем выдавать всякий раз, когда адрес электронной почты или пароль неверны.

Обновить (только для зарегистрированных пользователей)

Имейте в виду, что вам может потребоваться заменить токен Bearer: ezJhbGci0i…

$ curl -X POST http://localhost:3000/auth/refresh -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NSwiZW1haWwiOiJlbG9uQGdtYWlsLmNvbSIsImlhdCI6MTY0NjU5NzY2NiwiZXhwIjoxNjc4MTMzNjY2fQ.fBG9QJKOUIvrB1iaDDs-2gJKH_qephHNRtJyiKG-aVk"
Server Response:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NSwiZW1haWwiOiJlbG9uQGdtYWlsLmNvbSIsImlhdCI6MTY0NjU5NzkyOSwiZXhwIjoxNjc4MTMzOTI5fQ.B8hCJmzoiLKSlZLVDIllLjYm5z7N9soDHSdRg_Iax2s

Эта конечная точка выдаст ошибку 403 (запрещено), если токен JWT недействителен или не определен.

Изменение имени пользователя

$ curl -X PUT http://localhost:3000/user/name -H "Content-Type: application/json" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6NSwiZW1haWwiOiJlbG9uQGdtYWlsLmNvbSIsImlhdCI6MTY0NjU5NzkyOSwiZXhwIjoxNjc4MTMzOTI5fQ.B8hCJmzoiLKSlZLVDIllLjYm5z7N9soDHSdRg_Iax2s" -d '{"name": "Super Elon"}'
Server Response:
{"id": 1, "email": "[email protected]", "name": "Super Elon", "lastLoginAt": "2022-03-06T20:18:49.356Z"}

Как видите, мы изменили имя пользователя.

Наконец, мы закончили! Я выложил проект на GitHub. Проверьте это.

Спасибо, что прочитали мою статью о том, как использовать аутентификацию JWT в NestJS. Надеюсь, вы смогли узнать что-то новое.

Ваше здоровье!

Надеюсь, вам понравилось это читать. Если вы хотите поддержать меня как писателя, рассмотрите возможность подписки стать участником Medium. Всего 5 долларов в месяц, и вы получаете неограниченный доступ к Medium.

Хотите поддержать меня? Купи мне кофе.