Включите исполнение JavaScript в браузере, чтобы запустить приложение.

Гайд по Nest.js: что это такое и как написать свой первый код

Один из самых популярных языков программирования JavaScript имеет множество достоинств, но есть у него и явные недостатки: его применяют только для создания веб-страниц. Нивелировать этот изъян помогает фреймворк Nest.js. Он предлагает разработчикам множество возможностей для того, чтобы создавать легко масштабируемые серверные приложения. В статье разберемся, как с его помощью написать свой первый код.

Что такое Nest.js

Nest.js — это фреймворк, который был создан для написания кода серверных приложений. С его помощью разработчики могут писать бэкенд, используя уже известные и понятные им концепции из JavaScript и TypeScript. С ним будет легко работать тем, кто знаком с Angular, так как Nest.js наследовал от него многие концепции: модули, провайдеры, декораторы.

Преимущества и особенности Nest.js

  • Каждое приложение разбито на отдельные модули, что позволяет его легко масштабировать, вносить изменения. Эти модули легко переносить из одной части приложения в другую, импортировать в другие модули. 
  • В фреймворке реализована инъекция зависимостей, что упрощает работу с ними. 
  • Код, написанный на Nest.js, чистый и понятный благодаря использованию декораторов. 
  • В работе с Nest.js будет легко разобраться, если вы работали с JavaScript и TypeScript.
  • Nest.js хорошо работает совместно с популярными инструментами и библиотеками:  GraphQL, WebSockets.
  • В Nest.js встроена своя система маршрутизации. Это позволяет программисту выстраивать свои маршруты для различных действий в приложении, обрабатывать запросы к ним. 
  • В Nest.js есть встроенные инструменты для тестирования приложений, которые помогают проверять их надежность. 

Создание проекта в Nest.js

Для начала потребуется установить сам фреймворк Node.js и npm (Node Package Manager). Затем мы устанавливаем Nest CLI ― инструмент для упрощения работы с командной строкой. Для этого введите команду: 

npm install -g @nestjs/cli

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

nest new project-name

Здесь project-name замените на название вашего проекта. После выполнения команды надо будет выбрать пакет менеджера и режим работы TypeScript. Чтобы перейти в директорию, где лежит ваш проект, нужна команда: 

cd project-name

А чтобы запустить его, напишите: 

npm run start

Чтобы увидеть ваш проект в работе, откройте браузер и введите в строку адрес: http://localhost:3000

В проекте сразу будет сформировано дерево из папок. Каждая предназначена для хранения своих частей проекта: 

  • src/: ― здесь лежит основной код проекта; 
  • app.controller.ts: ― контроллер для HTTP-запросов;
  • app.service.ts: ― папка для хранения бизнес-логики;
  • app.module.ts: ― директория с основным модулем вашего приложения;
  • main.ts: ― точка, через которую пользователь будет попадать в приложение; 
  • test/: ― папка для хранения тестов. 

Чтобы создать новый компонент в вашем проекте, понадобится команда: 

nest generate module users

Она создает модуль «пользователи», внутри которого будут лежать файлы и папки.  

Контроллеры как точка входа в Nest.js

В Nest.js контроллер ― это точка входа для пользователей. Контроллеры обрабатывают входящие запросы и определяют, какой маршрут или метод будет использоваться. Для примера мы создадим контроллер для управления статьями с помощью Nest CLI. Прописываем команду: 

nest generate controller articles
javascript

Она создает файл articles.controller.ts в папке src/articles. Открываем этот файл, чтобы прописать там методы для создания новой статьи и получения их полного списка:

import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';

import { ArticlesService } from './articles.service';

import { CreateArticleDto } from './create-article.dto';

import { UpdateArticleDto } from './update-article.dto';

@Controller('articles')

export class ArticlesController {

constructor(private readonly articlesService: ArticlesService) {}

  @Get()

  findAll() {

    return this.articlesService.findAll();

  }

  @Get(':id')

  findOne(@Param('id') id: string) {

    return this.articlesService.findOne(+id);

  }

  @Post()

  create(@Body() createArticleDto: CreateArticleDto) {

    return this.articlesService.create(createArticleDto);

  }

  @Put(':id')

  update(@Param('id') id: string, @Body() updateArticleDto: UpdateArticleDto) {

    return this.articlesService.update(+id, updateArticleDto);

  }

  @Delete(':id')

  remove(@Param('id') id: string) {

    return this.articlesService.remove(+id);

  }

}
javascript

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

nest generate service articles
javascript

После выполнения этой команды у нас появится файл articles.service.ts. Далее пропишем методы для работы со статьями: их загрузки, удаления и обновления данных: 

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

import { CreateArticleDto } from './create-article.dto';

import { UpdateArticleDto } from './update-article.dto';

@Injectable()

export class ArticlesService {

  private articles = [];

  create(createArticleDto: CreateArticleDto) {

    const article = {

      id: this.articles.length + 1,

      ...createArticleDto,

    };

    this.articles.push(article);

    return article;

  }

  findAll() {

    return this.articles;

  }

  findOne(id: number) {

    const article = this.articles.find(article => article.id === id);

    if (!article) {

      throw new NotFoundException(`Article with ID ${id} not found`);

    }

    return article;

  }

  update(id: number, updateArticleDto: UpdateArticleDto) {

    const article = this.findOne(id);

    Object.assign(article, updateArticleDto);

    return article;

  }

  remove(id: number) {

    const index = this.articles.findIndex(article => article.id === id);

    if (index === -1) {

      throw new NotFoundException(`Article with ID ${id} not found`);

    }

    this.articles.splice(index, 1);

    return { message: `Article with ID ${id} deleted` };

  }

}
javascript

Теперь регистрируем модуль в основном приложении. Прописываем код в файл app.module.ts.

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

import { ArticlesModule } from './articles/articles.module';

@Module({

  imports: [ArticlesModule],

})

export class AppModule {}
javascript

Теперь запускаем наше приложение командой:

npm run start
javascript

Посмотреть результат можно в браузере по адресу http://localhost:3000/articles. А чтобы создать новую статью, необходимо отправить POST-запрос по адресу http://localhost:3000/articles

Для чего используются декораторы в фреймворке Nest.js

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

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

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

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

Использование HTML-шаблонов в фреймворке Nest.js

HTML-шаблоны используются для создания и структурирования страниц в Nest.js. С их помощью можно создавать внешний вид сайта и его структуру, а также менять его содержание и другие данные. Главные преимущества шаблонов ― повторное использование кода для ускорения разработки, его понятная структура и легкая оптимизация сайта для запросов поисковиков. В этом примере мы будем использовать шаблонизатор Handlebars для рендеринга HTML-шаблонов в Nest.js.

Сначала установим необходимые зависимости:

npm install @nestjs/platform-express handlebars
javascript

Затем в main.ts укажем Handlebars как движок представлений данных:

import { NestFactory } from '@nestjs/core';

import { AppModule } from './app.module';

import { join } from 'path';

import * as express from 'express';

import * as exphbs from 'express-handlebars';

async function bootstrap() {

  const app = await NestFactory.create(AppModule);

  app.useStaticAssets(join(__dirname, '..', 'public'));

  app.setBaseViewsDir(join(__dirname, '..', 'views'));

  app.engine('hbs', exphbs({

    defaultLayout: 'main',

    extname: '.hbs',

  }));

  app.setViewEngine('hbs');

  await app.listen(3000);

}

bootstrap();
javascript

Далее создадим структуру папок нашего проекта: 

project-name/

├── src/

│   ├── app.controller.ts

│   ├── app.module.ts

│   ├── app.service.ts

│   └── main.ts

├── views/

│   ├── layouts/

│   │   └── main.hbs

│   └── index.hbs

├── public/

│   └── styles.css

├── package.json

└── tsconfig.json
javascript

Далее создаем основной макет и шаблон главной страницы:

handlebars

<!-- views/layouts/main.hbs -->

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="UTF-8">

  <meta name="viewport" content="width=device-width, initial-scale=1.0">

  <title>{{title}}</title>

  <link rel="stylesheet" href="/styles.css">

</head>

<body>

  {{{body}}}

</body>

</html>

handlebars

<!-- views/index.hbs -->

<h1>Добро пожаловать на сайт</h1>

<p>Этот сайт создан с использованием Handlebars.</p>

Настала пора создать контроллер, который будет рендерить наш шаблон:

typescript

// src/app.controller.ts

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

@Controller()

export class AppController {

  @Get()

  @Render('index')

  getHello(): { title: string } {

    return { title: 'Nest.js with Handlebars' };

  }

}
html

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

npm run start
javascript

Смотрим результат в браузере по ссылке http://localhost:3000.

Статьи и их просмотр в Nest.js

Теперь создадим целую систему, которую используем для управления статьями и их данными в Nest.js: модуль, контроллер и сервис для управления. Кроме того, придется настроить маршруты: чтобы создавать, редактировать и удалять статьи. 

Сначала создадим новый модуль для управления данными статей:

nest generate module articles
javascript

Теперь создадим контроллер и сервис для модуля articles:

nest generate controller articles

nest generate service articles
javascript

Далее используем Data Transfer Object, чтобы создавать и обновлять данные статьи. Для этого создадим файлы create-article.dto.ts и update-article.dto.ts в папке articles:

typescript

// src/articles/create-article.dto.ts

export class CreateArticleDto {

  readonly title: string;

  readonly content: string;

}

// src/articles/update-article.dto.ts

export class UpdateArticleDto {

  readonly title?: string;

  readonly content?: string;

}
ts

Создаем методы, которые будем использовать для управления данными статей в сервисе articles.service.ts:

typescript

// src/articles/articles.service.ts

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

import { CreateArticleDto } from './create-article.dto';

import { UpdateArticleDto } from './update-article.dto';

@Injectable()

export class ArticlesService {

  private articles = [];

  create(createArticleDto: CreateArticleDto) {

    const article = {

      id: this.articles.length + 1,

      ...createArticleDto,

    };

    this.articles.push(article);

    return article;

  }

  findAll() {

    return this.articles;

  }

  findOne(id: number) {

    const article = this.articles.find(article => article.id === id);

    if (!article) {

      throw new NotFoundException(`Article with ID ${id} not found`);

    }

    return article;

  }

  update(id: number, updateArticleDto: UpdateArticleDto) {

    const article = this.findOne(id);

    Object.assign(article, updateArticleDto);

    return article;

  }

  remove(id: number) {

    const index = this.articles.findIndex(article => article.id === id);

    if (index === -1) {

      throw new NotFoundException(`Article with ID ${id} not found`);

    }

    this.articles.splice(index, 1);

    return { message: `Article with ID ${id} deleted` };

  }

}
ts

Далее необходимо прописать маршруты для всех действий с данными статей в контроллере articles.controller.ts:

typescript

// src/articles/articles.controller.ts

import { Controller, Get, Post, Body, Param, Put, Delete } from '@nestjs/common';

import { ArticlesService } from './articles.service';

import { CreateArticleDto } from './create-article.dto';

import { UpdateArticleDto } from './update-article.dto';

@Controller('articles')

export class ArticlesController {

  constructor(private readonly articlesService: ArticlesService) {}

  @Post()

  create(@Body() createArticleDto: CreateArticleDto) {

    return this.articlesService.create(createArticleDto);

  }

  @Get()

  findAll() {

    return this.articlesService.findAll();

  }

  @Get(':id')

  findOne(@Param('id') id: string) {

    return this.articlesService.findOne(+id);

  }

  @Put(':id')

  update(@Param('id') id: string, @Body() updateArticleDto: UpdateArticleDto) {

    return this.articlesService.update(+id, updateArticleDto);

  }

  @Delete(':id')

  remove(@Param('id') id: string) {

    return this.articlesService.remove(+id);

  }

}
ts

В финале регистрируем модуль ArticlesModule в основном модуле приложения app.module.ts:

typescript

// src/app.module.ts

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

import { ArticlesModule } from './articles/articles.module';

@Module({

  imports: [ArticlesModule],

})

export class AppModule {}
ts

После этого мы можем проверить, как работает наш сервис. Запускаем приложение: 

npm run start
javascript

Смотрим результат в браузере по ссылке http://localhost:3000. А используя Postman, мы можем выполнить необходимые запросы к нашему API.

Заключение

Как мы увидели на примере, Nest.js позволяет разрабатывать простые приложения с небольшим количеством кода. Но максимальную пользу фреймворк приносит тогда, когда на нем пишут  приложения с сотней модулей, где используется большое количество данных. Такое приложение легко понять даже начинающему разработчику (из-за чистоты кода), а также легко развивать, дополняя новыми модулями. Особенно разработчикам нравится в Nest.js то, что в нем есть встроенные обработчики ошибок, поддержка большого количества протоколов, кроме HTTP.