Что такое NestJS

Содержание

Введение и установка

Введение в Nest.js и его основные принципы

Что такое Nest.js

Nest.js — это фреймворк для создания масштабируемых серверных приложений на Node.js. Он предоставляет набор инструментов и абстракций для упрощения разработки и тестирования больших систем.

Основные принципы

  1. Модульность: Nest.js использует модульную архитектуру, что позволяет легко разделять приложение на логические части и повторно использовать код.
  2. Dependency Injection: Фреймворк использует систему внедрения зависимостей для улучшения тестируемости и повторного использования кода.
  3. Декораторы: Nest.js активно использует декораторы для добавления метаданных и дополнительной функциональности к классам и методам.
  4. Типизация: Интеграция с TypeScript обеспечивает строгую типизацию и улучшает читаемость и поддерживаемость кода.
  5. Тестирование: Фреймворк предоставляет удобные инструменты для юнит-тестирования и интеграционного тестирования.
  6. Архитектурные шаблоны: Nest.js поддерживает различные архитектурные шаблоны, такие как MVC, REST и GraphQL, что делает его гибким инструментом для различных задач.

Почему выбрать Nest.js?

  • Производительность: Оптимизированный для высокой производительности и низкой задержки.
  • Экосистема: Широкий выбор встроенных модулей и интеграций с популярными библиотеками и фреймворками.
  • Сообщество: Активное и растущее сообщество разработчиков, множество обучающих материалов и курсов.

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

Установка CLI Nest.js

Что такое Nest CLI

Nest CLI (Command Line Interface) — это инструмент командной строки, предоставляемый фреймворком Nest.js для ускорения и автоматизации процесса разработки. С его помощью можно создавать новые проекты, генерировать модули, контроллеры, сервисы и многое другое.

Установка

Для установки Nest CLI, вам потребуется Node.js и npm (Node Package Manager). Если у вас их ещё нет, установите с официального сайта Node.js.

После установки Node.js и npm, откройте терминал и выполните следующую команду:

npm i -g @nestjs/cli

Эта команда установит Nest CLI глобально, что позволит использовать его в любой директории.

Проверка установки

Чтобы убедиться, что CLI установлен корректно, выполните:

nest --version

Если всё прошло успешно, вы увидите текущую установленную версию Nest CLI.

Создание нового проекта

С помощью CLI можно легко создать новый проект. Для этого используется команда:

nest new project-name

Эта команда создаст новую директорию с именем project-name, инициализирует там новый проект на Nest.js и установит все необходимые зависимости.

Дополнительные возможности

Nest CLI предлагает множество других команд и опций для управления проектом, таких как:

  • nest generate или nest g для генерации различных типов файлов (контроллеры, модули, сервисы и т.д.).
  • nest build для компиляции TypeScript-кода в JavaScript.
  • nest start для запуска приложения.

Архитектура и структура проекта

Что такое трехслойная архитектура

Трехслойная архитектура — это один из популярных подходов к организации кода в программной инженерии. Она разделяет приложение на три основных слоя: представление (Presentation), бизнес-логика (Business Logic), и доступ к данным (Data Access).

Как это реализовано в Nest.js
  1. Контроллеры (Presentation Layer)
    • Отвечают за обработку входящих HTTP-запросов и формирование ответов.
    • Используют сервисы для выполнения бизнес-логики.
    • Обычно не содержат бизнес-логики, а лишь делегируют её выполнение сервисам.
  2. Сервисы (Business Logic Layer)
    • Содержат основную бизнес-логику приложения.
    • Могут взаимодействовать с репозиториями для доступа к данным.
    • Являются местом для применения правил валидации, кэширования, транзакций и т.д.
  3. Модели и Репозитории (Data Access Layer)
    • Модели представляют структуры данных.
    • Репозитории отвечают за взаимодействие с базой данных или другими источниками данных.
    • В Nest.js часто используются с ORM-библиотеками, такими как TypeORM или Mongoose.
Преимущества трехслойной архитектуры в Nest.js
  • Чистота кода: Легче поддерживать и расширять код, когда он хорошо структурирован.
  • Тестируемость: Каждый слой можно тестировать независимо.
  • Переиспользование: Бизнес-логика в сервисах и логика доступа к данным в репозиториях могут быть легко переиспользованы.
  • Гибкость: Легче заменить один из слоёв без необходимости переписывать весь код.

Структура проекта и файлов в Nest.js

Стандартная структура проекта

После создания нового проекта с помощью Nest CLI, вы получите следующую структуру каталогов и файлов:

my-nest-project/
├── src/
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   └── main.ts
├── test/
│   └── app.e2e-spec.ts
├── package.json
├── tsconfig.json
└── nest-cli.json

Описание основных файлов и каталогов

  1. src/: Эта директория содержит исходный код вашего приложения.
    • main.ts: Это точка входа в ваше приложение. Здесь создается экземпляр Nest и запускается HTTP-сервер.
    • app.module.ts: Главный модуль вашего приложения. Здесь определяются импорты, провайдеры и контроллеры на уровне приложения.
    • app.controller.ts и app.service.ts: Примеры контроллера и сервиса, созданные по умолчанию.
  2. test/: Директория для тестов.
    • app.e2e-spec.ts: Пример теста end-to-end.
  3. package.json: Файл с описанием проекта и зависимостями.
  4. tsconfig.json: Конфигурация TypeScript для вашего проекта.
  5. nest-cli.json: Конфигурация CLI Nest.js.

Дополнительные каталоги и файлы

В процессе разработки могут добавляться дополнительные каталоги и файлы, такие как:

  • modules/: Директория для хранения модулей приложения.
  • config/: Конфигурационные файлы (например, для базы данных, авторизации и т.д.).
  • utils/: Утилиты и вспомогательные функции.

Соглашения именования

Nest.js следует определенным соглашениям именования, таким как .module.ts для модулей, .controller.ts для контроллеров и .service.ts для сервисов. Это облегчает навигацию по проекту и понимание его структуры.

Запуск проекта и проверка работы в Nest.js

Запуск проекта

После создания проекта и установки всех зависимостей, вы можете запустить его, используя следующую команду в терминале:

npm run start

Или, если вы предпочитаете использовать Nest CLI:

nest start

Эти команды запустят ваше приложение на Node.js и вы сможете обратиться к нему по адресу http://localhost:3000, если не указано иное.

Режимы разработки

Hot Reload: Для автоматической перезагрузки приложения при изменении файлов используйте команду:

npm run start:dev

Отладка: Для запуска приложения в режиме отладки:

npm run start:debug

Проверка работы

Чтобы убедиться, что ваше приложение работает, откройте веб-браузер и перейдите по адресу http://localhost:3000. Если все настроено правильно, вы должны увидеть ответ от вашего приложения, который обычно генерируется контроллером AppController.

Логирование

Nest.js предоставляет встроенные инструменты для логирования, которые можно использовать для отслеживания активности приложения. По умолчанию, при запуске приложения в консоли будут отображаться базовые логи.

Тестирование

Вы можете запустить юнит-тесты, используя команду:

npm test

Эта команда запустит все тесты, находящиеся в каталоге test/.

Создание модулей в Nest.js с помощью CLI

Что такое модуль в Nest.js?

В Nest.js модули являются основной организационной единицей. Каждый модуль агрегирует различные части приложения, такие как контроллеры, провайдеры и сервисы, и может импортировать другие модули.

Создание модуля с помощью CLI

Чтобы создать новый модуль, вы можете использовать следующую команду CLI:

nest generate module my-module

или сокращенный вариант:

nest g mo my-module

Эта команда создаст новый файл модуля my-module.module.ts и автоматически зарегистрирует его в главном модуле приложения (app.module.ts), если не указано иное.

Пример структуры

После выполнения команды создания модуля, структура вашего проекта может выглядеть так:

src/
├── my-module/
│   └── my-module.module.ts
├── app.module.ts
└── main.ts

Работа с модулями

  • Импорт и экспорт: В файле модуля вы можете указать, какие другие модули необходимы для его работы, а также какие части (контроллеры, провайдеры и т.д.) должны быть доступны для других модулей.
  • Ленивая загрузка: Nest.js поддерживает ленивую загрузку модулей, что может быть полезно для оптимизации производительности больших приложений.
  • Модульные зависимости: Вы можете инжектировать один модуль в другой, создавая таким образом сложные и гибкие зависимости между различными частями вашего приложения.

Работа с контроллерами и сервисами в Nest.js

Создание контроллера и сервиса с помощью CLI

Создание контроллера

Чтобы создать новый контроллер, используйте следующую команду CLI:

nest generate controller my-controller

или сокращенный вариант:

nest g co my-controller

Эта команда создаст новый файл контроллера my-controller.controller.ts в директории src/controllers (или в корневой директории src, если вы не изменяли структуру проекта).

Создание сервиса

Для создания нового сервиса используйте команду:

nest generate service my-service

или сокращенный вариант:

nest g s my-service

Эта команда создаст новый файл сервиса my-service.service.ts.

Автоматическая регистрация

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

Пример структуры

После выполнения этих команд, ваша структура проекта может выглядеть примерно так:

src/
├── controllers/
│   └── my-controller.controller.ts
├── services/
│   └── my-service.service.ts
├── app.module.ts
└── main.ts

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

Декораторы в Nest.js: Подробный обзор

Декораторы для классов

Эти декораторы применяются непосредственно к классам и служат для определения их роли в приложении.

  • @Controller(prefix): Определяет класс как контроллер и задает его префикс маршрута.
  • @Module({}): Определяет класс как модуль и позволяет настроить его зависимости.

Декораторы методов

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

  • @Get(route): Обозначает метод как обработчик GET-запросов.
  • @Post(route): Обозначает метод как обработчик POST-запросов.
  • @UseGuards(Guard): Применяет охранник (guard) к методу для выполнения проверок безопасности или бизнес-логики.

Декораторы параметров

Эти декораторы применяются к аргументам методов и позволяют извлекать данные из запроса.

  • @Req(): Предоставляет объект запроса от Express.
  • @Query(param): Извлекает определенный параметр запроса.
  • @Body(key): Извлекает значение из тела запроса по ключу.

Декораторы свойств

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

  • @Inject(token): Инжектирует зависимость, ассоциированную с данным токеном.
  • @InjectRepository(Entity): Инжектирует репозиторий TypeORM для работы с определенной сущностью.

Пример использования

// Классовый декоратор
@Module({
    controllers: [AppController],
    providers: [AppService]
})
export class AppModule {}

// Методовые и параметровые декораторы
@Controller('app')
export class AppController {
    constructor(private readonly appService: AppService) {}

    @Get('data')
    getData(@Query('id') id: string) {
        return this.appService.getData(id)
    }

    // Свойственный декоратор
    @Inject()
    private readonly configService: ConfigService
}

Таблица декораторов

Классовые декораторы
------------------------------------------------
| @Controller()         | Определяет класс как контроллер и задает префикс маршрута. 
| @Module()             | Определяет класс как модуль и позволяет настроить его зависимости.
| @Injectable()         | Определяет класс как провайдер, который может быть инжектирован.
| @Global()             | Определяет модуль как глобальный.
| @WebSocketGateway()   | Определяет класс как WebSocket шлюз.
| @Catch()              | Определяет класс как фильтр исключений.

Методовые декораторы
------------------------------------------------
| @Get()            | Обозначает метод как обработчик GET-запросов.
| @Post()           | Обозначает метод как обработчик POST-запросов.
| @Put()            | Обозначает метод как обработчик PUT-запросов.
| @Delete()         | Обозначает метод как обработчик DELETE-запросов.
| @Patch()          | Обозначает метод как обработчик PATCH-запросов.
| @UseGuards()      | Применяет охранник (guard) к методу.
| @UsePipes()       | Применяет каналы (pipes) к методу.
| @UseInterceptors()| Применяет перехватчики (interceptors) к методу.
| @UseFilters()     | Применяет фильтры исключений к методу.

Параметровые декораторы
------------------------------------------------
| @Req()            | Инжектирует объект запроса от Express.
| @Res()            | Инжектирует объект ответа от Express.
| @Query()          | Извлекает параметры запроса.
| @Param()          | Извлекает параметры маршрута.
| @Body()           | Извлекает тело запроса.
| @Headers()        | Извлекает заголовки запроса.
| @Session()        | Извлекает данные сессии.

Свойственные декораторы
------------------------------------------------
| @Inject()             | Инжектирует зависимость, ассоциированную с данным токеном.
| @InjectRepository()   | Инжектирует репозиторий TypeORM для определенной сущности.

Работа с маршрутами и HTTP-методами

Основы маршрутизации

Маршрутизация в Nest.js основана на декораторах, которые применяются к методам контроллера. Эти декораторы указывают, какой HTTP-метод должен быть использован для доступа к определенному маршруту.

HTTP-методы и их декораторы

  • GET: @Get('route') — получение данных.
  • POST: @Post('route') — создание нового ресурса.
  • PUT: @Put('route') — обновление существующего ресурса.
  • DELETE: @Delete('route') — удаление ресурса.
  • PATCH: @Patch('route') — частичное обновление ресурса.

Примеры

@Controller('users')
export class UsersController {
    @Get()
    findAll() {
        // Возвращает всех пользователей
    }

    @Get(':id')
    findOne(@Param('id') id: string) {
        // Возвращает одного пользователя по ID
    }

    @Post()
    create(@Body() createUserDto: CreateUserDto) {
        // Создает нового пользователя
    }

    @Put(':id')
    update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
        // Обновляет пользователя с указанным ID
    }

    @Delete(':id')
    remove(@Param('id') id: string) {
        // Удаляет пользователя с указанным ID
    }
}

Добавление метода для получения всех пользователей

Шаг 1: Определение контроллера

Для начала убедимся, что у вас есть контроллер, в котором будет размещен метод. Если контроллера нет, создайте его с помощью CLI:

nest generate controller users

Это создаст файл users.controller.ts и зарегистрирует его в соответствующем модуле.

Шаг 2: Импорт зависимостей

Импортируйте необходимые декораторы и типы данных:

import { Controller, Get } from '@nestjs/common';

Шаг 3: Определение метода

Добавьте метод findAll в ваш контроллер и декорируйте его с помощью @Get():

@Controller('users')
export class UsersController {
    @Get()
    findAll(): string {
        return 'Возвращает всех пользователей'
    }
}

В этом примере метод findAll просто возвращает строку, но на практике вы, скорее всего, будете взаимодействовать с сервисом для получения данных из базы данных.

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

Шаг 4: (Опционально) Интеграция с сервисом

Если у вас есть сервис для работы с пользователями, вы можете интегрировать его в ваш контроллер:

import { UsersService } from './users.service'

@Controller('users')
export class UsersController {
    constructor(private readonly usersService: UsersService) {}

    @Get()
    findAll(): string {
        return this.usersService.findAll()
    }
}

В этом случае метод findAll из UsersService должен быть реализован так, чтобы возвращать список всех пользователей.

Теперь, когда метод findAll добавлен в ваш контроллер, он будет обрабатывать GET-запросы по маршруту /users и возвращать всех пользователей.

Добавление метода для получения пользователя по ID

Шаг 1: Импорт зависимостей

Убедитесь, что у вас импортированы необходимые декораторы и типы:

import { Controller, Get, Param } from '@nestjs/common';

Шаг 2: Добавление метода в контроллер

В том же контроллере UsersController, где вы добавили метод findAll, добавьте новый метод findOne. Используйте декоратор @Get() с параметром маршрута для указания ID пользователя:

@Controller('users')
export class UsersController {
    // ... (предыдущий код)

    @Get(':id')
    findOne(@Param('id') id: string): string {
        return `Возвращает пользователя с ID ${id}`
    }
}

Шаг 3: (Опционально) Интеграция с сервисом

Не является обязательным для минимальной рабочей реализации

Если у вас есть сервис для работы с пользователями, метод findOne может вызывать соответствующий метод в этом сервисе:

import { UsersService } from './users.service'

@Controller('users')
export class UsersController {
    constructor(private readonly usersService: UsersService) {}

    // ... (предыдущий код)

    @Get(':id')
    findOne(@Param('id') id: string): string {
        return this.usersService.findOne(id)
    }
}

В этом случае, метод findOne из UsersService должен быть реализован так, чтобы возвращать пользователя по указанному ID.

Заключение

Теперь у вас есть метод findOne, который обрабатывает GET-запросы по маршруту /users/:id и возвращает пользователя с соответствующим ID. Это базовая реализация, и в реальном приложении вы, скорее всего, будете работать с базой данных для получения данных пользователя.

Использование декоратора @Param для извлечения параметров

Основная идея

Декоратор @Param в Nest.js используется для извлечения параметров из URL маршрута. Этот декоратор применяется к аргументам методов в контроллере и позволяет получить доступ к динамическим частям URL.

Пример URL с параметрами

Предположим, у вас есть URL вида /users/42, где 42 — это ID пользователя. Этот ID является динамическим параметром, и его можно извлечь с помощью декоратора @Param.

Пример кода

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

import { Controller, Get, Param } from '@nestjs/common'

@Controller('users')
export class UsersController {
    @Get(':id')
    findOne(@Param('id') id: string): string {
        return `Пользователь с ID ${id}`
    }
}

В этом примере метод findOne в контроллере UsersController обрабатывает GET-запросы по маршруту /users/:id. Декоратор @Param('id') извлекает значение id из URL и передает его в метод findOne.

Извлечение нескольких параметров

Вы также можете извлекать несколько параметров из URL. Например, если у вас есть маршрут /users/42/posts/3, вы можете извлечь оба параметра так:

@Get(':userId/posts/:postId')
findPost(@Param('userId') userId: string, @Param('postId') postId: string): string {
    return `Пост с ID ${postId} пользователя с ID ${userId}`;
}

Заключение

Декоратор @Param является мощным инструментом для работы с динамическими маршрутами в Nest.js. Он позволяет извлекать один или несколько параметров из URL и использовать их в вашей бизнес-логике.

Введение в Dependency Injection

Объяснение принципов Dependency Injection

Что такое Dependency Injection (DI)

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

Принципы DI в Nest.js

Nest.js использует DI как один из своих основных архитектурных принципов. Это позволяет разделять логику приложения на различные слои и упрощает тестирование. В Nest.js DI реализовано через декораторы и метаданные.

Контейнеры и провайдеры

В основе DI в Nest.js лежат два ключевых элемента: контейнер DI и провайдеры. Контейнер DI отвечает за управление зависимостями и их жизненным циклом. Провайдеры — это классы или значения, которые можно инжектировать в другие классы.

Скоупы

Nest.js поддерживает различные скоупы для провайдеров: синглтон (по умолчанию), транзиентный и запрос-ориентированный. Это позволяет управлять временем жизни инжектируемых зависимостей.

Декораторы для DI

  • @Injectable(): Отмечает класс как инжектируемый провайдер.
  • @Inject(): Используется для инжекции провайдера в свойство или конструктор класса.
  • @Optional(): Отмечает зависимость как необязательную.

Пример

// Сервис
@Injectable()
export class UsersService {
    findAll() {
        return 'Все пользователи'
    }
}

// Контроллер
@Controller('users')
export class UsersController {
    constructor(private readonly usersService: UsersService) {}

    @Get()
    findAll() {
        return this.usersService.findAll()
    }
}

В этом примере UsersService инжектируется в UsersController через конструктор. Декоратор @Injectable() указывает, что UsersService является инжектируемым провайдером.

Автоматическое внедрение сервиса в контроллер

Как это работает

В Nest.js автоматическое внедрение зависимостей (DI) работает "из коробки" благодаря использованию TypeScript и его системы декораторов и метаданных. Когда вы создаёте сервис с декоратором @Injectable() и затем указываете его в конструкторе контроллера, Nest автоматически создает экземпляр этого сервиса и внедряет его в контроллер.

Пример

Допустим, у вас есть сервис UsersService, который вы хотите использовать в UsersController.

// users.service.ts
import { Injectable } from '@nestjs/common'

@Injectable()
export class UsersService {
    findAll() {
        return 'Список всех пользователей'
    }
}

Чтобы автоматически внедрить этот сервис в контроллер, вам нужно сделать следующее:

// users.controller.ts
import { Controller, Get } from '@nestjs/common'
import { UsersService } from './users.service'

@Controller('users')
export class UsersController {
    constructor(private readonly usersService: UsersService) {}

    @Get()
    findAll() {
        return this.usersService.findAll()
    }
}

Здесь UsersService указан в конструкторе UsersController, и благодаря этому Nest.js автоматически внедряет его. Обратите внимание на модификатор private readonly: это не только хорошая практика в TypeScript, но и позволяет Nest.js корректно проанализировать зависимости.

Регистрация провайдеров

Не забудьте зарегистрировать UsersService в соответствующем модуле через поле providers:

// users.module.ts
import { Module } from '@nestjs/common'
import { UsersController } from './users.controller'
import { UsersService } from './users.service'

@Module({
    controllers: [UsersController],
    providers: [UsersService]
})
export class UsersModule {}

Теперь UsersService будет автоматически внедрен в UsersController, и вы можете использовать его методы внутри контроллера.

Работа с данными и сервисами в Nest.js

Симуляция базы данных с помощью массива

Что это и зачем нужно?

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

Пример симуляции базы данных

Допустим, у нас есть сервис для работы с пользователями. Мы можем симулировать базу данных с помощью массива в этом сервисе.

// users.service.ts
import { Injectable } from '@nestjs/common'

@Injectable()
export class UsersService {
    private readonly users = [
        { id: 1, name: 'Alice' },
        { id: 2, name: 'Bob' },
        { id: 3, name: 'Charlie' }
    ]

    findAll() {
        return this.users
    }

    findOne(id: number) {
        return this.users.find(user => user.id === id)
    }

    create(name: string) {
        const newUser = { id: Date.now(), name }
        this.users.push(newUser)
        return newUser
    }
}

В этом примере массив users служит в качестве симулированной базы данных. Методы findAll, findOne и create работают с этим массивом, имитируя операции чтения и записи в реальную базу данных.

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

Реализация методов для поиска и создания пользователей в сервисе

Основная структура сервиса

Для начала, убедимся, что у вас есть базовая структура сервиса, который будет работать с данными. Этот сервис должен быть декорирован аннотацией @Injectable():

import { Injectable } from '@nestjs/common'

@Injectable()
export class UsersService {
    // ...
}

Симулированная база данных

Для простоты, мы будем использовать массив для хранения данных о пользователях:

private readonly users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
    { id: 3, name: 'Charlie' },
];

Метод для поиска всех пользователей

Создадим метод findAll, который будет возвращать всех пользователей:

findAll() {
    return this.users;
}

Метод для поиска пользователя по ID

Добавим метод findOne, который будет искать пользователя по его ID:

findOne(id: number) {
    return this.users.find(user => user.id === id);
}

Метод для создания нового пользователя

Теперь добавим метод create, который будет создавать нового пользователя:

create(name: string) {
    const newUser = { id: Date.now(), name };
    this.users.push(newUser);
    return newUser;
}

Полный код сервиса

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

import { Injectable } from '@nestjs/common';

@Injectable()
export class UsersService {
  private readonly users = [
    { id: 1, name: 'Alice' },
    { id: 2, name: 'Bob' },
    { id: 3, name: 'Charlie' },
  ];

  findAll() {
    return this.users;
  }

  findOne(id: number) {
    return this.users.find(user => user.id === id);
  }

  create(name: string) {
    const newUser = { id: Date.now(), name };
    this.users.push(newUser);
    return newUser;
  }
}

Эти методы теперь можно использовать в вашем контроллере для обработки HTTP-запросов.

Работа с запросами и DTO (Data Transfer Object)

Создание нового пользователя с использованием HTTP POST

Что такое HTTP POST?

HTTP POST — это метод запроса, который используется для отправки данных на сервер для создания нового ресурса. В контексте RESTful API, POST часто используется для создания новых сущностей.

Добавление метода в контроллер

Чтобы обработать POST-запрос для создания нового пользователя, добавьте новый метод в ваш контроллер. Используйте декоратор @Post() для указания, что этот метод должен обрабатывать POST-запросы.

import { Controller, Post } from '@nestjs/common'
import { UsersService } from './users.service'

@Controller('users')
export class UsersController {
    constructor(private readonly usersService: UsersService) {}

    @Post()
    create() {
        return this.usersService.create('New User')
    }
}

В этом примере метод create вызывает метод create из UsersService, который мы реализовали ранее. Здесь мы просто хардкодим имя нового пользователя как "New User", но на практике это значение, скорее всего, будет приходить в теле HTTP-запроса.

Тестирование

Вы можете тестировать этот метод, используя инструменты типа Postman или curl, отправляя POST-запрос на URL /users.

Использование декоратора @Body для извлечения данных из тела запроса

Что такое @Body?

Декоратор @Body() используется для извлечения данных из тела HTTP-запроса. Это полезно, например, при создании новых сущностей через POST-запросы, когда данные отправляются в теле запроса в формате JSON.

Пример использования в контроллере

Допустим, вы хотите создать нового пользователя и данные для этого приходят в теле POST-запроса. Вы можете использовать декоратор @Body() для доступа к этим данным:

import { Controller, Post, Body } from '@nestjs/common'
import { UsersService } from './users.service'

@Controller('users')
export class UsersController {
    constructor(private readonly usersService: UsersService) {}

    @Post()
    create(@Body() userData: { name: string }) {
        return this.usersService.create(userData.name)
    }
}

В этом примере @Body() извлекает объект userData из тела запроса. Этот объект ожидается с полем name, которое затем передается в метод create сервиса UsersService.

Типизация и валидация

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

interface CreateUserDto {
    name: string;
    // другие поля...
}

// ...

@Post()
create(@Body() userData: CreateUserDto) {
    return this.usersService.create(userData.name);
}

Теперь метод create ожидает, что тело запроса будет соответствовать структуре CreateUserDto.

Работа с ответами и статусами

Использование декораторов для указания возможных HTTP-статусов

Что такое HTTP-статусы и зачем их указывать?

HTTP-статусы — это трехзначные числовые коды, которые сервер возвращает клиенту для указания результата обработки запроса. Они помогают клиенту понять, был ли запрос успешным или произошла ошибка, и какого рода эта ошибка.

Декораторы для HTTP-статусов

В Nest.js есть специальные декораторы для указания возможных HTTP-статусов ответа. Это полезно не только для управления поведением вашего API, но и для автоматической генерации документации.

@HttpCode

Декоратор @HttpCode() позволяет явно указать HTTP-статус, который должен быть возвращен. Например:

import { Post, HttpCode } from '@nestjs/common';

@Post('create')
@HttpCode(201)
create() {
    // логика создания ресурса
}

В этом примере, при успешном выполнении метода create, будет возвращен HTTP-статус 201 (Created).

@ApiResponse

Декоратор @ApiResponse() используется для описания возможных ответов API. Он особенно полезен при использовании инструментов для документирования API, таких как Swagger.

import { Post, HttpCode, ApiResponse } from '@nestjs/common';

@Post('create')
@HttpCode(201)
@ApiResponse({ status: 201, description: 'The record has been successfully created.'})
@ApiResponse({ status: 403, description: 'Forbidden.'})
create() {
    // логика создания ресурса
}

В этом примере мы указали, что метод может вернуть статус 201 при успешном создании ресурса, или 403, если операция запрещена.

Добавление типов ответов для улучшения документации

Зачем это нужно?

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

Как добавить типы ответов

В Nest.js вы можете использовать декораторы, такие как @ApiResponse, для указания типов ответов. Это делается с помощью свойства type в объекте, передаваемом в @ApiResponse.

Пример с простым типом

Если ваш метод возвращает простой тип данных (например, строку), вы можете указать это так:

import { Get, Controller, HttpCode, ApiResponse } from '@nestjs/common'

@Controller()
export class AppController {
    @Get()
    @HttpCode(200)
    @ApiResponse({ status: 200, description: 'Success', type: String })
    getHello(): string {
        return 'Hello World!'
    }
}
Пример с объектом

Если ваш метод возвращает объект или массив, вы можете указать тип с помощью класса или интерфейса:

import { Post, HttpCode, ApiResponse } from '@nestjs/common'

class CreateUserResponse {
    id: number
    name: string
}

@Controller('users')
export class UsersController {
    @Post('create')
    @HttpCode(201)
    @ApiResponse({ status: 201, description: 'User created', type: CreateUserResponse })
    create(): CreateUserResponse {
        return { id: 1, name: 'Alice' }
    }
}

В этом примере, CreateUserResponse описывает структуру ответа, и это будет отражено в документации.

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

Если метод возвращает массив, вы можете указать это с помощью isArray: true:

@Get('all')
@ApiResponse({ status: 200, description: 'Get all users', type: CreateUserResponse, isArray: true })
findAll(): CreateUserResponse[] {
    return [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }];
}

Работа с Query Parameters

Введение в Query Parameters и их использование

Что такое Query Parameters?

Query Parameters (параметры запроса) — это пары ключ-значение, которые добавляются в URL после символа ? и разделяются символом &. Они часто используются для передачи дополнительной информации на сервер, например, для фильтрации данных, пагинации или сортировки.

Пример URL с Query Parameters

http://example.com/users?age=30&status=active

В этом примере age и status являются Query Parameters, и они имеют значения 30 и active соответственно.

Зачем они нужны?

Query Parameters позволяют делать запросы более гибкими. Они могут быть использованы на сервере для выполнения различных операций без изменения URL-пути, что делает их идеальным выбором для фильтрации данных, пагинации и других задач.

Использование в Nest.js

В Nest.js для работы с Query Parameters часто используется декоратор @Query(). Этот декоратор позволяет извлекать параметры запроса прямо в методах контроллера.

Простой пример
import { Controller, Get, Query } from '@nestjs/common'

@Controller('users')
export class UsersController {
    @Get()
    findAll(@Query() query: any) {
        // Логика обработки query parameters
    }
}

В этом примере @Query() извлекает все Query Parameters из URL и помещает их в объект query.

Декоратор @Query для извлечения параметров из URL

Что делает @Query?

Декоратор @Query() в Nest.js используется для извлечения параметров запроса (Query Parameters) из URL. Эти параметры часто используются для фильтрации, сортировки или пагинации данных.

Как использовать @Query

Извлечение всех параметров запроса

Если вы хотите получить все Query Parameters, используйте @Query() без аргументов. Это вернет вам объект с всеми параметрами запроса.

import { Controller, Get, Query } from '@nestjs/common'

@Controller('items')
export class ItemsController {
    @Get()
    findAll(@Query() query: any) {
        // теперь query содержит все параметры запроса
    }
}
Извлечение конкретного параметра

Вы также можете извлечь конкретный параметр запроса, указав его имя как аргумент для @Query().

import { Controller, Get, Query } from '@nestjs/common'

@Controller('items')фimport { Controller, Get, Query } from '@nestjs/common'

@Controller('items')
export class ItemsController {
    @Get()
    findByCategory(@Query('category') category: string) {
        // теперь переменная category содержит значение параметра запроса "category"
    }
}

export class ItemsController {
    @Get()
    findAll(@Query() query: any) {
        // теперь query содержит все параметры запроса
    }
}

В этом примере, если URL будет выглядеть как /items?category=books, переменная category получит значение 'books'.

Использование с типизацией

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

interface QueryParams {
    category: string
    limit: number
}

@Controller('items')
export class ItemsController {
    @Get()
    findAll(@Query() query: QueryParams) {
        // теперь query соответствует интерфейсу QueryParams
    }
}

Дополнительные возможности и направления для изучения

Обзор Guards для аутентификации и авторизации

Guards в Nest.js служат для реализации логики аутентификации и авторизации. Они определяют, может ли пользователь получить доступ к определенным ресурсам или выполнить определенные действия. Guards активируются перед входом в методы контроллера и могут прервать запрос, если условия не выполнены.

Введение в Interceptors для дополнительной логики

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

Использование существующих middleware из экосистемы Express.js

Nest.js совместим с экосистемой Express.js, что позволяет использовать уже существующие middleware из этой экосистемы. Это дает возможность легко интегрировать проверенные решения и библиотеки, ускоряя разработку и улучшая качество кода.

Общее Заключение

Nest.js представляет собой мощный и гибкий фреймворк для создания серверных приложений на Node.js. Он предлагает ряд инструментов и паттернов для решения типичных задач разработки, начиная от базовых элементов, таких как контроллеры, сервисы и модули, до более продвинутых функций, включая аутентификацию через Guards, дополнительную логику через Interceptors и интеграцию с экосистемой Express.js.

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

Если вы ищете инструмент для создания масштабируемых, поддерживаемых и тестированных серверных приложений на Node.js, Nest.js является отличным выбором. Он предлагает современные концепции и практики разработки, а также обширную экосистему и активное сообщество.

В общем, Nest.js — это не просто еще один фреймворк; это целая экосистема, которая может значительно ускорить ваш процесс разработки и повысить качество вашего кода.

Написать комментарий