Перевод статьи подготовлен в преддверии старта курса «Разработчик Node.js».




У современных разработчиков есть много альтернатив, когда речь заходит о создании веб-сервисов и других серверных приложений. Node стал крайне популярным выбором, однако многие программисты предпочитают более надежный язык, чем JavaScript, особенно те, кто пришел из современных объектно-ориентированных языков, например, таких как C#, C++ или Java. Если TypeScript просто хорошо подходит NodeJS , то фреймворк NestJS выводит его на совершенно новый уровень, предоставляя современные инструменты бэкенд-разработчику для создания долговечных и высокопроизводительных приложений с использованием компонентов, провайдеров, модулей и других полезных высокоуровневых абстракций.

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

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


Для работы с Nest нужна среда Node. Если у вас ее еще нет, зайдите на их сайт и скачайте ее.

Установить фреймворк достаточно просто:

$ npm i -g @nestjs/cli

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

$ nest new nest-js-example

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

Точка входа приложения


Основной файл, который конфигурирует и запускает сервер – это src/main.ts:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
	
    const app = await NestFactory.create(AppModule);
    await app.listen(3000);
}

bootstrap();

Этот файл импортирует класс NestFactory, который используется для создания приложения, и основной файл AppModule (с которым мы в скором времени познакомимся), а затем загружает приложение, инстанцируя его, и слушает на 3000 порту.

App Module


Файл, в котором задекларированы компоненты приложения, называется src/app.module.ts:

import { Module } from '@nestjs/common';
import { ItemsController } from './items/items.controller';
import { ItemsService } from './items/items.service';

@Module({
    imports: [],
    controllers: [ ItemsController ],
    providers: [ ItemsService ],
})

export class AppModule {}

Это файл, где остальные компоненты импортируются и декларируются в Модуль, который импортируется в предыдущем файле (main.ts). Инструменты Nest CLI автоматически обновят этот файл по мере необходимости, когда будет дана команда создать новый компонент. Здесь импортируются контроллер и сервис для items и добавляются в модуль.

Items Controller


Следующий файл, с которым мы ознакомимся – это src/items/items.controller.ts:

import { Controller, Req, Get, Post, Body } from '@nestjs/common'
import { CreateItemDto } from './dto/create-item.dto'
import { ItemsService } from './items.service'
import { Item } from './items.interface'

@Controller('items')
export class ItemsController {

    constructor(private readonly itemsService: ItemsService) {}

    @Post()
    create(@Body() data: CreateItemDto): Object {
        return this.itemsService.create(data)
    }

    @Get()
    findAll(): Array<Item> {
        return this.itemsService.findAll()
    }
}

Этот файл определяет контроллер для создания item’ов и получения списка ранее созданных. Здесь импортируется несколько ключевых компонентов:

  • CreateItemDto: Объект Data-Transfer, который определяет как данные item’ов будут отправляться по сети (т.е. это структура данных JSON);
  • ItemsService: Provider, который обрабатывает манипуляции или хранение данных Item;
  • Item: Интерфейс, который определяет внутреннюю структуру данных для Item;

Декоратор @Controller('items') указывает фреймворку, что этот класс будет обслуживать конечную точку REST /items, а конструктор ItemsController берет экземпляр ItemsService, который используется внутри для обслуживания двух HTTP методов:

  • POST /items (создает новый item из JSON-запроса);
  • GET /items (получения списка ранее созданных item’ов).

Запросы к этим двум методам обрабатываются методами create и FindAll, которые привязаны к соответствующим методам HTTP с помощью декораторов @Post() и @Get(). Дополнительные методы тоже могут поддерживаться декораторами аналогичным образом, например, @Put() или @Delete() и т.д.

Интерфейсы объекта Item


Дальше разберемся с двумя файлами, которые определяют интерфейсы для хранения item, один для внутреннего использования, такого как проверка типа по время компиляции (Item), и внешний интерфейс для определения ожидаемой структуры входящего JSON (CreateItemDto):

export interface Item {
	
    name: string,
    description: string,
    price: number
}

export class CreateItemDto {

    @IsNotEmpty()
    readonly name: string;

    @IsNotEmpty()
    readonly description: string;

    @IsNotEmpty()
    readonly price: number;
}

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

Класс CreateItemDto отражает свойства Item, декорируя каждое свойство @IsNotEmpty(), чтобы гарантировать, что все эти свойства запрашиваются конечной точкой REST API.

Все свойства обоих классов строго типизированы, что является одним из главных преимуществ TypeScript (отсюда и название). На первый взгляд это повышает уровень понимания контекста, а еще значительно сокращает время разработки при правильном использовании вместе с инструментами анализа кода (такими как IntelliSense в VSCode). Особенно характерно это для больших проектов с сотнями или даже тысячами различных классов и интерфейсов. Для тех, кто не обладает совершенной фотографической памятью с бесконечной емкостью (например, для меня), так гораздо проще, чем пытаться запомнить тысячи конкретных деталей.

Сервис Items


Самый последний – сервис для создания и получения items: items.service.dart:

import { Injectable } from '@nestjs/common'
import { Item } from './items.interface'

@Injectable()
export class ItemsService {

    private items: Array<Item> = []

    create(item: Item): Object {

        this.items.push(item) 
        return { id: this.items.length.toString() }
    }

    findAll(): Array<Item> {

        return this.items;
    }
}

Класс ItemsService определяет простой массив объектов Item, который будет служить в качестве in-memory хранилища данных для нашего проекта-примера. Два метода, которые пишут и читают из этого хранилища это:

  • create (сохраняет Item в список и возвращает его id);
  • findAll (возвращает список ранее созданных объектов Item).

Тестируем


Чтобы запустить сервер, используйте стандартную команду npm run start. Когда приложение запущено, его можно протестировать с помощью отправки HTTP-запросов через CURL:

$ curl -X POST localhost:3000/items -d '{"name":"trinket", "description":"whatever", "price": 42.0}'

Выполнение этой команды вернет ответ в формате JSON с id,созданного item. Чтобы показать список item’ов, которые уже созданы используйте:

$ curl localhost:3000/items

GET-запрос к /items, приведенный выше, вернет ответ в формате JSON с информацией об item’ах, которые уже хранятся в памяти. Ответ должен выглядеть как-то так:

[{"{\"name\":\"trinket\", \"description\":\"whatever\", \"price\": 42.0}":""}]

Заключение


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

Чтобы узнать больше, посетите сайт NestJS.
Спасибо, что ознакомились с моей статьей. Счастливого кодинга!