Вы находитесь на странице: 1из 23

 Все статьи | Код

Гайд по Nest.js: что это такое и как


написать свой первый код

Nest.js — самый важный и популярный фреймворк для создания


серверных веб-приложений Node.js. В этом большом гайде мы
поможем новичкам сделать первый шаг в освоении этого
фреймворка для серверного JavaScript и расскажем, в чем вообще
особенности Nest.js.

Введение
Создадим проект
Точка входа
Статьи
Добавляем HTML몭шаблоны
Устанавливаем шаблонизатор
Первый шаблон
Добавляем просмотр статей
Добавляем статью
Заключение

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

Знание JavaScript на среднем уровне


Знание основ TypeScript, особенно синтаксиса декораторов
Базовые знания Node.js. Желательно, опыт с Express.js
Знание базовых понятий MVC (Model-View-Controller)

Также нам понадобятся установленные на компьютер:

Node.js версии 10+. Инструкция по установке


Docker

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


Развертывание СУБД в контейнере — один из самых простых способов.
Но если вы хотите использовать другой способ, или же работать с уже
имеющейся СУБД — нет проблем. В таком случае Docker вам не
понадобится.

Прокачивайте свой уровень программирования:


На Хекслете есть несколько десятков треков — специальных
курсов для опытных программистов, позволяющие повысить
уровень компетентности разработчика в разных направлениях.

Введение
Зачем нам нужен фреймворк?

Большинство задач в программировании — типовые. Если не брать


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

Первым супер-популярным веб-фреймворком для Node.js был


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

Для примера мы напишем небольшой блог, в котором можно


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

Создадим проект
Чтобы каждый раз не приходилось настраивать проект с нуля,
разработчики Nest.js вооружили нас консольной утилитой —
@nestjs/cli. Установим ее глобально:

npm i @nestjs/cli ‐g 

Теперь в вашей директории с проектами выполним команду nest new,


указав имя проекта.

nest new nestjs‐getting‐started 

Когда cli закончит свою работу, мы можем перейти в директорию


nestjs-ge몭ting-s몭ar몭ed и посмотреть, что получилось:
Все основные зависимости уже установлены, сборка TypeScript
настроена. Кроме этого, создана папка src, которая и будет
интересовать нас больше всего. Здесь уже есть несколько файлов —
это демонстрационное приложение от создателей Nest.js.

Самое приятное, что приложение уже рабочее и его можно запустить.


Делается это при помощи команды

npm run start:dev 

Теперь, если ввести в адресную строку браузера http://localhost:3000,


то мы увидим возвращённую сервером фразу Hello world!.

Запущенное командой npm run start:dev приложение будет


перезагружаться каждый раз, когда изменяется исходный код проекта.

Остановить работающее приложение можно комбинацией клавиш


Ctrl+C.

Точка входа
Точкой входа в приложение на Nest.js, как и в любом другом MVC몭
подобном фреймворке, являются контроллеры. Пока в приложении
имеется один: app.controller.몭s:

import { Controller, Get } from '@nestjs/common'; 
import { AppService } from './app.service'; 

@Controller() 
export class AppController { 
  constructor(private readonly appService: AppService) {} 

 @Get() 
 getHello(): string { 
   return this.appService.getHello(); 
 } 

Обратите внимание, что класс AppController и метод getHello()


помечены декораторами @Controller() и @Get(). Nest.js очень широко
использует декораторы, поэтому к ним лучше сразу привыкать. Они
позволяют писать приложение в более декларативном ключе,
указывая что мы хотим сделать, и оставляя детали реализации
фреймворку.

Если читатель «плавает» в теме декораторов, рекомендуем к


прочтению эту статью.

Декоратор @Get() говорит, что когда в приложение придёт HTTP몭


запрос методом GET на роут '/' (это значение по умолчанию, поэтому
его можно не указывать), его следует направить в метод getHello().

Соответственно, значение, которое вернётся из метода, будет


отправлено в теле ответа. Код ответа по умолчанию для GET몭запросов
— 200.

Метод getHello() и конструктор мы пока удалим. Взамен создадим


метод index(), который будет возвращать статьи. Контроллер
приобретёт такой вид:
import { Controller, Get } from '@nestjs/common'; 

@Controller() 
export class AppController { 
  @Get() 
  index() { 
    return { articles: [] }; 
  } 

Теперь, зайдя на h몭tp://localhost:3000 мы вместо Hello world увидим


такой JSON몭


  "articles": [] 

Обратите внимание, что мы возвращаем не «голый» массив, а


заворачиваем его в объект c ключом articles. Нам это пригодится чуть
дальше.

Статьи
Пришло время наполнить блог статьями. Но что такое «статья»?
Каждая статья — объект, который будет иметь уникальный
идентификатор, заголовок и контент.

Создадим в папке src новый файл article.model.몭s и в нём опишем класс


статьи:

export class Article { 
  id: number; 

  title: string; 
  content: string; 

  constructor (title: string, content: string, id?: number) { 
    this.id = id; 
    this.title = title; 
    this.content = content; 
  } 

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


будем подключать базу данных.

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


Создадим файл articles.몭s:

import { Article } from './article.model'; 

export const articles: Article[] = [ 
 new Article('The first one', 'Lorem ipsum ....', 1), 
 new Article('The second one', 'Lorem ipsum ....', 2) 

Осталось вернуть массив статей из метода index() в контроллере:

import { articles } from './articles'; 

@Controller() 
export class AppController { 
  @Get() 
  index() { 
    return { articles }; 
  } 

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


  "articles": [ 
    { 
      "id": 1, 
      "title": "The first one", 
      "content": "Lorem ipsum ...." 
    }, 
    { 
      "id": 2, 
      "title": "The second one", 
      "content": "Lorem ipsum ...." 
    } 
  ] 

Добавляем HTML몭шаблоны
Для того, чтобы пользователю было комфортнее работать с сайтом,
сервер должен вернуть браузеру ответ не в формате json, а в виде
html-страницы.

Создадим в корне проекта директорию views — в ней мы будем


размещать HTML몭шаблоны.

Устанавливаем шаблонизатор
Nest.js по умолчанию не устанавливает никаких движков
шаблонизации, так как не знает, какой именно по душе пользователю.
Мы для этого примера возьмём pug (ранее известный как jade). Этот
пакет нам придется установить самостоятельно:

npm install pug 
Чтобы подключить pug, перепишем функцию boo몭strap (main.ts):

import { join } from 'path'; 
import { NestExpressApplication } from '@nestjs/platform‐express'; 

async function bootstrap() { 
  /* 
  * Добавим параметр типа к методу create, показывая, что мы хотим работать
  * с объектом app, как с приложением express. 
  */ 
  const app = await NestFactory.create<NestExpressApplication>(AppModule

  // Сообщим приложению, где искать наши views. 
  app.setBaseViewsDir(join(__dirname, '../views')); 

  // И укажем, какой шаблонизатор использовать 
  app.setViewEngine('pug'); 
  await app.listen(3000); 

Первый шаблон
Наш первый шаблон index.pug будет выглядеть так:

doctype html 
html(lang="en") 
  head 
    meta(charset="UTF‐8") 
    meta(http‐equiv="X‐UA‐Compatible", content="IE=edge") 
    meta(name="viewport", content="width=device‐width, initial‐scale=1.0")
    title SuperBlog! 
  body 
    h1 Welcome to SuperBlog! 
    // Конструкция "each" позволяет перебрать массив и для каждого элемента
    ul 
      each article in articles 
        // #{...} — интерполяция. Так шаблонизатор понимает, что сюда надо 
        li #{article.title}  <a href="...">Читать</a> 

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


это на усмотрение читателя.

В контроллере добавим к методу index декоратор @Render()


(импортируется из пакета @nestjs/common):

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

@Controller() 
export class AppController { 
  @Get() 
  @Render('index') 
  index() { 
    return { articles }; 
  } 

@Render('index') указывает фреймворку, что те данные, которые


возвращаются из метода нужно не просто вернуть браузеру, а
использовать для отрисовки шаблона с названием index (расширение
не важно).

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


— html-страницу с двумя статьями.

Читайте также:
Что такое webpack ex몭ernals и как их настроить.

Добавляем просмотр статей


В app.controller.몭s добавим в импорт декоратор @Param() и создадим
новый метод:

import { Render, Param, ParseIntPipe } from '@nestjs/common'; 

... 
@Get(':id') 
@Render('article') 
getById(@Param('id', ParseIntPipe) id: number) { 
  return articles.find(article => article.id === id); 

Строка :id в декораторе @Get() означает, что в этот метод будут


направлены запросы на корневой роут с параметром, например: GET
h몭tp://localhost/1.

При помощи декоратора @Param() мы можем достать этот


идентификатор из URL, преобразовать его к числу (ParseIntPipe) и
использовать для поиска нужной статьи.

Кроме декоратора @Param() в Nest.js есть также декораторы @Query()


для query-параметров и @Body() для тела запроса.

Добавим в папку views шаблон article.pug:

doctype html 
html(lang="en") 
  head 
    meta(charset="UTF‐8") 
    meta(http‐equiv="X‐UA‐Compatible", content="IE=edge") 
    meta(name="viewport", content="width=device‐width, initial‐scale=1.0")
    title #{title} 
  style 
  body 
    h1 #{title} 
    p #{content} 
    a(href="/") Назад 
Шапка будет та же самая, только в тэге title мы выведем название
статьи. В теле страницы — ожидаемо title и content, а также ссылка
«назад» — на главную страницу.

Осталось починить ссылки на главной странице:

li #{article.title}  
  a(href=article.id) Читать 

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


«Читать» у любой из статей и увидеть содержимое статьи. А при клике
на «Назад» — вернуться к списку статей. Также можно зайти на
страницу по прямой ссылке, например h몭tp://localhost:3000/1.

Добавляем статью
Чтобы создать новую статью, браузер должен отправить на сервер
title и content POST몭запросом. Чтобы реализовать это, добавим в
контроллер перед методом article два новых метода:

import { Body, Post, Redirect } from '@nestjs/common'; 
import { Article } from './article.model'; 

@Get('create') 
@Render('create‐article') 
getForm(): void { 
  return; 

@Post('articles') 
@Redirect('/', 301) 
create(@Body() body: any): void { 
  const id = articles.length + 1; 
  const article = new Article(body.title, body.content, id); 
  articles.push(article); 

Метод getForm() не требует никакого возвращаемого значения — он


просто возвращает статический HTML.

В методе create() реализуем добавление статьи в коллекцию. Думаю,


читатель уже догадался, что декоратор @Post() означает одноимённый
HTTP몭глагол. Декоратор @Body() указывает фреймворку, что данные
для параметра нужно брать из тела запроса. И, наконец, декоратор
@Redirect(‘/’, 301) говорит, что после добавления статьи требуется
переадресовать пользователя. Первый аргумент - ‘/’ обозначает
корневой роут (то есть, главную страницу), а 301 — код ответа.

Шаблон crea몭e-article.pug будет выглядеть так:

doctype html 
html 
  head 
    title Новая статья 
  style 
  body 
    h1 Новая статья 
    form(method="POST" action="/articles") 
      input(type="text" name="title") 
      input(type="text" name="content") 
      button(type="submit") Создать 

Добавление статей реализовано. На страницу добавления можно


попасть, введя в адресной строке h몭tp://localhost:3000/crea몭e. Чтобы
было чуть удобнее, добавим ссылку на эту страничку вниз списка
статей. index.pug приобретёт такой вид:

doctype html 
html(lang="en") 
  head 
    meta(charset="UTF‐8") 
    meta(http‐equiv="X‐UA‐Compatible", content="IE=edge") 
    meta(name="viewport", content="width=device‐width, initial‐scale=1.0")
    title SuperBlog! 
  body 
    h1 Welcome to SuperBlog! 
    ul 
      each article in articles 
        li #{article.title}  
          a(href=article.id) Читать 
    a(href="create") Создать 

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

Декларативное программирование при помощи декораторов


Встроенный DI몭контейнер (кратко о DI и IoC)
Мощные специализированные middleware: интерцепторы, пайпы,
гарды
Механизм обработки ошибок «из коробки»
Поддержка любых протоколов, помимо HTTP. На Nest.js можно
писать сервисы на основе RabbitMQ, Na몭s, Kafka или даже просто
TCP몭протокола.

Чтобы освоить все это, а также более продвинутые возможности


Nest.js, читателю предстоит пройти немалый путь, и автор надеется,
что эта статья станет уверенным первым шагом на нем.

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


качестве примера, вы можете посмотреть здесь.
Никогда не останавливайтесь:
В программировании говорят, что нужно постоянно учиться даже
для того, чтобы просто находиться на месте. Развивайтесь с нами
— на Хекслете есть сотни курсов по разработке на разных языках
и технологиях.

Eonae
07 июля 2022

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

Профессия с нуля

Фронтенд-разработчик
Разработка фронтенд-компонентов для веб-
приложений

 19 января ὕ 10 месяцев

Профессия Новый с нуля

Онлайн-буткемп. Фронтенд-
разработчик
Интенсивное обучение профессии в режиме полного
дня

 9 февраля ὕ 4 месяца

Профессия с нуля

Python-разработчик
Python-разработчик
Разработка веб-приложений на Django

 19 января ὕ 10 месяцев

Профессия с нуля

Java-разработчик
Разработка приложений на языке Java

 19 января ὕ 10 месяцев

Профессия с нуля

PHP몭разработчик
Разработка веб-приложений на Laravel

 19 января ὕ 10 месяцев

Профессия с нуля

Инженер по тестированию
Ручное тестирование веб-приложений

 19 января ὕ 4 месяца

Профессия с нуля

Node.js-разработчик
Разработка бэкенд-компонентов для веб-приложений

 19 января ὕ 10 месяцев

Профессия с нуля
Fulls몭ack-разработчик
Разработка фронтенд- и бэкенд-компонентов для веб-
приложений

 19 января ὕ 16 месяцев

Профессия c опытом

Разработчик на Ruby on Rails


Создание веб-приложений со скоростью света

 19 января ὕ 5 месяцев

Профессия с нуля

Верстальщик
Верстка с использованием последних стандартов CSS

 в любое время ὕ 5 месяцев

Профессия В разработке с нуля

Аналитик данных
Сбор, анализ и интерпретация данных

 дата определяется ὕ 8 месяцев

ALSO ON RU.HEXLET.IO/BLOG

Как работать в IntelliJ Восьмая неделя на Как страховщик ушел


IDEA c WSL Хекслете к тем, кто отнял у него К чему готов

2 месяца назад • 1 коммента... 2 месяца назад • 1 коммента... месяц назад • 1 комментарий 20 дней назад
WSL — подсистема Восьмая неделя обучения. Расскажу, как потерял Мы узнали у э
Windows для Linux. В Трудности, наблюдения, работу во время современным
статье рассказываем, ощущения. пандемии, но не отчаялся: программиров
зачем ее использовать, попробовал свои силы в будет с их люб

8 Комментариев 
1
Войти

G Присоединиться к обсуждению...

ВОЙТИ С ПОМОЩЬЮ ИЛИ ЧЕРЕЗ DISQUS ?

Имя

1 Share Лучшие Новые Старые

Vlad Gromov ⏲ год назад edited ⚑ −


VG В этой статье полно ошибок. Отличие моего решения в том, что я подключался к БД
postgres напрямую, без докер контейнера.

В app.controller.ts в методе @Post('articles') забыли передать id:


@Post('articles')
@Redirect('/', 301)
async create(
@Body() body: { title: string; content: string; id: number },
): Promise<void> {
const article = new Article(body.title, body.content, body.id);
await article.save();
}

В app.module.ts не указали entities Article который ранее создавали:


@Module({
imports: [
TypeOrmModule.forRoot({
type: 'postgres',
host: 'localhost',
показать больше

2 0 • Ответить • Поделиться ›

RisDeep > Vlad Gromov


⏲ год назад
− ⚑
а почему работа с базой данных в контроллере, а не в сервисе?
0 0 • Ответить • Поделиться ›

Denis Bondarenko ⏲ 19 дней назад


− ⚑
Denis Bondarenko −
Спасибо, за статью. Хорошо доступно и понятно. Интересный фреймворк.
0 0 • Ответить • Поделиться ›

DIMANVAZ04 ⏲ месяц назад edited


− ⚑
D // Сообщим приложению, где искать наши views.
app.setBaseViewsDir(join(__dirname, '../views'));
---
вместо этой строки, на StackOver몭ow рекомендовали:
app.useStaticAssets(join(__dirname, '../views'));
0 0 • Ответить • Поделиться ›

Ivan Gagarinov Модератор > DIMANVAZ04


⏲ месяц назад
− ⚑
useStaticAssets используется для статический ресурсов. Это могут быть
скрипты js, html, изображения и т.д.. Это ресурсы, которые будут доступны
клиенту. А setBaseViewsDir указывается для шаблонов, которые могут
заполняться данными, и из этих шаблонов генерируются страницы.
0 0 • Ответить • Поделиться ›

Данил Замешаев ⏲ 8 месяцев назад


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

И пожалуйста исправьте ошибки в тексте статьи, как минимум те, на которые уже
указали 8 месяцев(!) назад.
Это же не сложно, попробовать выполнить действия по вашей же статье - и увидеть
как что-то не работает.

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


технологией.
0 0 • Ответить • Поделиться ›

Сергей К. > Данил Замешаев


⏲ 7 месяцев назад
− ⚑
СК Данил, по части Docker на Хекслете есть целый курс –
https://ru.hexlet.io/course.... Он как раз даёт понимание темы работы с
образами.
0 0 • Ответить • Поделиться ›

Святослав Иванов
⏲ 8 месяцев назад
Модератор > Данил Замешаев − ⚑

Добрый день! да, с ошибками сейчас разберемся, спасибо большое!


0 0 • Ответить • Поделиться ›
Подписаться Privacy Не продавайте мои данные
О нас Учиться

Карьера в Хекслете Профессии с нуля

Магазин мерча Все курсы

Индивидуальное обучение
Документы
Корпоративное обучение
Условия использования

Соглашение об обработке Читать


ПД Истории успеха
Публичная оферта Отзывы студентов
Акции Блог

Вопросы по урокам
8 800 100 22 47 бесплатно
по РФ Рекомендуемые книги

+7 495 085 28 38 Подписаться


бесплатно по Москве
    
ООО «Хекслет Рус»
432071, г. Ульяновск,
пр-т Нариманова, дом 1Г, оф.
23
ОГРН 1217300010476

Помощь

Справка

Вопросы и ответы

support@hexlet.io

Улучшить Хекслет

Наши проекты

Хекслет Колледж

Code Basics

Codeba몭tle

Hexlet Guides

Хекслет-резюме

Хекслет Runit

ἱ Язык

Вам также может понравиться