HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p><strong>Nest.js - самый важный и популярный фреймворк для создания серверных веб-приложений Node.js. В этом большом гайде мы поможем новичкам сделать первый шаг в освоении этого фреймворка для серверного JavaScript и расскажем, в чем вообще особенности<a>Nest.js</a>.</strong></p>
1 <p><strong>Nest.js - самый важный и популярный фреймворк для создания серверных веб-приложений Node.js. В этом большом гайде мы поможем новичкам сделать первый шаг в освоении этого фреймворка для серверного JavaScript и расскажем, в чем вообще особенности<a>Nest.js</a>.</strong></p>
2 <p>Для комфортного усвоения этого гайда вам потребуется:</p>
2 <p>Для комфортного усвоения этого гайда вам потребуется:</p>
3 <ul><li>Знание JavaScript на среднем уровне</li>
3 <ul><li>Знание JavaScript на среднем уровне</li>
4 <li>Знание основ TypeScript, особенно синтаксиса декораторов</li>
4 <li>Знание основ TypeScript, особенно синтаксиса декораторов</li>
5 <li>Базовые знания<a>Node.js</a>. Желательно, опыт с<em><a>Express.js</a></em></li>
5 <li>Базовые знания<a>Node.js</a>. Желательно, опыт с<em><a>Express.js</a></em></li>
6 <li>Знание базовых понятий<a>MVC</a>(Model-View-Controller)</li>
6 <li>Знание базовых понятий<a>MVC</a>(Model-View-Controller)</li>
7 </ul><p>Также нам понадобятся установленные на компьютер:</p>
7 </ul><p>Также нам понадобятся установленные на компьютер:</p>
8 <ul><li>Node.js версии 10+.<a>Инструкция по установке</a></li>
8 <ul><li>Node.js версии 10+.<a>Инструкция по установке</a></li>
9 <li>Docker</li>
9 <li>Docker</li>
10 </ul><p><em>Docker мы будем использовать для развертывания базы данных. Развертывание СУБД в контейнере - один из самых простых способов. Но если вы хотите использовать другой способ, или же работать с уже имеющейся СУБД - нет проблем. В таком случае Docker вам не понадобится.</em></p>
10 </ul><p><em>Docker мы будем использовать для развертывания базы данных. Развертывание СУБД в контейнере - один из самых простых способов. Но если вы хотите использовать другой способ, или же работать с уже имеющейся СУБД - нет проблем. В таком случае Docker вам не понадобится.</em></p>
11 <h2>Содержание</h2>
11 <h2>Содержание</h2>
12 <ul><li><a>Введение</a></li>
12 <ul><li><a>Введение</a></li>
13 <li><a>Создадим проект</a></li>
13 <li><a>Создадим проект</a></li>
14 <li><a>Точка входа</a></li>
14 <li><a>Точка входа</a></li>
15 <li><a>Статьи</a></li>
15 <li><a>Статьи</a></li>
16 <li><a>Добавляем HTML-шаблоны</a></li>
16 <li><a>Добавляем HTML-шаблоны</a></li>
17 <li><a>Добавляем просмотр статей</a></li>
17 <li><a>Добавляем просмотр статей</a></li>
18 <li><a>Добавляем статью</a></li>
18 <li><a>Добавляем статью</a></li>
19 <li><a>Заключение</a></li>
19 <li><a>Заключение</a></li>
20 </ul><h2>Введение</h2>
20 </ul><h2>Введение</h2>
21 <p><strong>Зачем нам нужен фреймворк?</strong></p>
21 <p><strong>Зачем нам нужен фреймворк?</strong></p>
22 <p>Большинство задач в программировании - типовые. Если не брать каких-то специфических бизнес-кейсов, скорее всего все возможные задачи уже решены другими программистами и не по одному разу. Не зря в среде программистов среде так часто можно услышать шутки о "велосипедостроении". Фреймворки помогают нам писать меньше шаблонного кода и сосредоточиться на том, какую полезную работу должна делать наша программа.</p>
22 <p>Большинство задач в программировании - типовые. Если не брать каких-то специфических бизнес-кейсов, скорее всего все возможные задачи уже решены другими программистами и не по одному разу. Не зря в среде программистов среде так часто можно услышать шутки о "велосипедостроении". Фреймворки помогают нам писать меньше шаблонного кода и сосредоточиться на том, какую полезную работу должна делать наша программа.</p>
23 <p>Первым супер-популярным веб-фреймворком для Node.js был<em>express.js</em>. Nest.js значительно расширяет его функциональность, добавляет декларативности, а также помогает разработчику строить приложение в соответствии с лучшими архитектурными практиками.</p>
23 <p>Первым супер-популярным веб-фреймворком для Node.js был<em>express.js</em>. Nest.js значительно расширяет его функциональность, добавляет декларативности, а также помогает разработчику строить приложение в соответствии с лучшими архитектурными практиками.</p>
24 <p>Для примера мы напишем небольшой блог, в котором можно просматривать и добавлять статьи. Конечно, это будет очень простое приложение, но его будет достаточно для первого знакомства.</p>
24 <p>Для примера мы напишем небольшой блог, в котором можно просматривать и добавлять статьи. Конечно, это будет очень простое приложение, но его будет достаточно для первого знакомства.</p>
25 <h2>Создадим проект</h2>
25 <h2>Создадим проект</h2>
26 <p>Чтобы каждый раз не приходилось настраивать проект с нуля, разработчики Nest.js вооружили нас консольной утилитой - @nestjs/cli. Установим ее глобально:</p>
26 <p>Чтобы каждый раз не приходилось настраивать проект с нуля, разработчики Nest.js вооружили нас консольной утилитой - @nestjs/cli. Установим ее глобально:</p>
27 <p>Теперь в вашей директории с проектами выполним команду nest new, указав имя проекта.</p>
27 <p>Теперь в вашей директории с проектами выполним команду nest new, указав имя проекта.</p>
28 <p>Когда cli закончит свою работу, мы можем перейти в директорию<em>nestjs-getting-started</em>и посмотреть, что получилось:</p>
28 <p>Когда cli закончит свою работу, мы можем перейти в директорию<em>nestjs-getting-started</em>и посмотреть, что получилось:</p>
29 <p>Все основные зависимости уже установлены, сборка TypeScript настроена. Кроме этого, создана папка<em>src</em>, которая и будет интересовать нас больше всего. Здесь уже есть несколько файлов - это демонстрационное приложение от создателей Nest.js.</p>
29 <p>Все основные зависимости уже установлены, сборка TypeScript настроена. Кроме этого, создана папка<em>src</em>, которая и будет интересовать нас больше всего. Здесь уже есть несколько файлов - это демонстрационное приложение от создателей Nest.js.</p>
30 <p>Самое приятное, что приложение уже рабочее и его можно запустить. Делается это при помощи команды</p>
30 <p>Самое приятное, что приложение уже рабочее и его можно запустить. Делается это при помощи команды</p>
31 <p>Теперь, если ввести в адресную строку браузера http://localhost:3000, то мы увидим возвращённую сервером фразу<strong>Hello world!</strong>.</p>
31 <p>Теперь, если ввести в адресную строку браузера http://localhost:3000, то мы увидим возвращённую сервером фразу<strong>Hello world!</strong>.</p>
32 <p><em>Запущенное командой npm run start:dev приложение будет перезагружаться каждый раз, когда изменяется исходный код проекта.</em></p>
32 <p><em>Запущенное командой npm run start:dev приложение будет перезагружаться каждый раз, когда изменяется исходный код проекта.</em></p>
33 <p>Остановить работающее приложение можно комбинацией клавиш Ctrl+C.</p>
33 <p>Остановить работающее приложение можно комбинацией клавиш Ctrl+C.</p>
34 <h2>Точка входа</h2>
34 <h2>Точка входа</h2>
35 <p>Точкой входа в приложение на Nest.js, как и в любом другом MVC-подобном фреймворке, являются<strong>контроллеры</strong>. Пока в приложении имеется один:<em>app.controller.ts</em>:</p>
35 <p>Точкой входа в приложение на Nest.js, как и в любом другом MVC-подобном фреймворке, являются<strong>контроллеры</strong>. Пока в приложении имеется один:<em>app.controller.ts</em>:</p>
36 <p>Обратите внимание, что класс AppController и метод getHello() помечены декораторами @Controller() и @Get(). Nest.js очень широко использует декораторы, поэтому к ним лучше сразу привыкать. Они позволяют писать приложение в более декларативном ключе, указывая<strong>что</strong>мы хотим сделать, и оставляя детали реализации фреймворку.</p>
36 <p>Обратите внимание, что класс AppController и метод getHello() помечены декораторами @Controller() и @Get(). Nest.js очень широко использует декораторы, поэтому к ним лучше сразу привыкать. Они позволяют писать приложение в более декларативном ключе, указывая<strong>что</strong>мы хотим сделать, и оставляя детали реализации фреймворку.</p>
37 <p><em>Если читатель "плавает" в теме декораторов, рекомендуем к прочтению<a>эту статью</a>.</em></p>
37 <p><em>Если читатель "плавает" в теме декораторов, рекомендуем к прочтению<a>эту статью</a>.</em></p>
38 <p>Декоратор @Get() говорит, что когда в приложение придёт HTTP-запрос методом GET на роут '/' (это значение по умолчанию, поэтому его можно не указывать), его следует направить в метод getHello().</p>
38 <p>Декоратор @Get() говорит, что когда в приложение придёт HTTP-запрос методом GET на роут '/' (это значение по умолчанию, поэтому его можно не указывать), его следует направить в метод getHello().</p>
39 <p>Соответственно, значение, которое вернётся из метода, будет отправлено в теле ответа. Код ответа по умолчанию для GET-запросов - 200.</p>
39 <p>Соответственно, значение, которое вернётся из метода, будет отправлено в теле ответа. Код ответа по умолчанию для GET-запросов - 200.</p>
40 <p>Метод getHello() и конструктор мы пока удалим. Взамен создадим метод index(), который будет возвращать статьи. Контроллер приобретёт такой вид:</p>
40 <p>Метод getHello() и конструктор мы пока удалим. Взамен создадим метод index(), который будет возвращать статьи. Контроллер приобретёт такой вид:</p>
41 <p>Теперь, зайдя на<a>http://localhost</a></p>
41 <p>Теперь, зайдя на<a>http://localhost</a></p>
42 <p>мы вместо<strong>Hello world</strong>увидим такой JSON:</p>
42 <p>мы вместо<strong>Hello world</strong>увидим такой JSON:</p>
43 <p><em>Обратите внимание, что мы возвращаем не "голый" массив, а заворачиваем его в объект c ключом articles. Нам это пригодится чуть дальше.</em></p>
43 <p><em>Обратите внимание, что мы возвращаем не "голый" массив, а заворачиваем его в объект c ключом articles. Нам это пригодится чуть дальше.</em></p>
44 <h2>Статьи</h2>
44 <h2>Статьи</h2>
45 <p>Пришло время наполнить блог статьями. Но что такое "статья"? Каждая статья - объект, который будет иметь уникальный идентификатор, заголовок и контент.</p>
45 <p>Пришло время наполнить блог статьями. Но что такое "статья"? Каждая статья - объект, который будет иметь уникальный идентификатор, заголовок и контент.</p>
46 <p>Создадим в папке<em>src</em>новый файл<em>article.model.ts</em>и в нём опишем класс статьи:</p>
46 <p>Создадим в папке<em>src</em>новый файл<em>article.model.ts</em>и в нём опишем класс статьи:</p>
47 <p><em>Почему id - необязательный параметр, выяснится позже - когда будем подключать базу данных.</em></p>
47 <p><em>Почему id - необязательный параметр, выяснится позже - когда будем подключать базу данных.</em></p>
48 <p>Теперь представим, что у нас в блоге уже есть парочка статей. Создадим файл<em>articles.ts</em>:</p>
48 <p>Теперь представим, что у нас в блоге уже есть парочка статей. Создадим файл<em>articles.ts</em>:</p>
49 <p>Осталось вернуть массив статей из метода index() в контроллере:</p>
49 <p>Осталось вернуть массив статей из метода index() в контроллере:</p>
50 <p>Если теперь запустить приложение, мы увидим в браузере следующее:</p>
50 <p>Если теперь запустить приложение, мы увидим в браузере следующее:</p>
51 <h2>Добавляем HTML-шаблоны</h2>
51 <h2>Добавляем HTML-шаблоны</h2>
52 <p>Для того, чтобы пользователю было комфортнее работать с сайтом, сервер должен вернуть браузеру ответ не в формате json, а в виде html-страницы.</p>
52 <p>Для того, чтобы пользователю было комфортнее работать с сайтом, сервер должен вернуть браузеру ответ не в формате json, а в виде html-страницы.</p>
53 <p>Создадим в корне проекта директорию<em>views</em>- в ней мы будем размещать HTML-шаблоны.</p>
53 <p>Создадим в корне проекта директорию<em>views</em>- в ней мы будем размещать HTML-шаблоны.</p>
54 <h4>Устанавливаем шаблонизатор</h4>
54 <h4>Устанавливаем шаблонизатор</h4>
55 <p>Nest.js по умолчанию не устанавливает никаких движков шаблонизации, так как не знает, какой именно по душе пользователю. Мы для этого примера возьмём<em><a>pug</a></em>(ранее известный как<em>jade</em>). Этот пакет нам придется установить самостоятельно:</p>
55 <p>Nest.js по умолчанию не устанавливает никаких движков шаблонизации, так как не знает, какой именно по душе пользователю. Мы для этого примера возьмём<em><a>pug</a></em>(ранее известный как<em>jade</em>). Этот пакет нам придется установить самостоятельно:</p>
56 <p>Чтобы подключить<em>pug</em>, перепишем функцию<em>bootstrap</em>(main.ts):</p>
56 <p>Чтобы подключить<em>pug</em>, перепишем функцию<em>bootstrap</em>(main.ts):</p>
57 <h4>Первый шаблон</h4>
57 <h4>Первый шаблон</h4>
58 <p>Наш первый шаблон<em>index.pug</em>будет выглядеть так:</p>
58 <p>Наш первый шаблон<em>index.pug</em>будет выглядеть так:</p>
59 <p><em>Добавление стилей выходит за рамки этой статьи, поэтому оставим это на усмотрение читателя.</em></p>
59 <p><em>Добавление стилей выходит за рамки этой статьи, поэтому оставим это на усмотрение читателя.</em></p>
60 <p>В контроллере добавим к методу index декоратор @Render() (импортируется из пакета @nestjs/common):</p>
60 <p>В контроллере добавим к методу index декоратор @Render() (импортируется из пакета @nestjs/common):</p>
61 <p>@Render('index') указывает фреймворку, что те данные, которые возвращаются из метода нужно не просто вернуть браузеру, а использовать для отрисовки шаблона с названием<em>index</em>(расширение не важно).</p>
61 <p>@Render('index') указывает фреймворку, что те данные, которые возвращаются из метода нужно не просто вернуть браузеру, а использовать для отрисовки шаблона с названием<em>index</em>(расширение не важно).</p>
62 <p>Готово. Теперь при запуске приложения мы увидим то, что и ожидали - html-страницу с двумя статьями.</p>
62 <p>Готово. Теперь при запуске приложения мы увидим то, что и ожидали - html-страницу с двумя статьями.</p>
63 <h2>Добавляем просмотр статей</h2>
63 <h2>Добавляем просмотр статей</h2>
64 <p>В<em>app.controller.ts</em>добавим в импорт декоратор @Param() и создадим новый метод:</p>
64 <p>В<em>app.controller.ts</em>добавим в импорт декоратор @Param() и создадим новый метод:</p>
65 <p>Строка :id в декораторе @Get() означает, что в этот метод будут направлены запросы на корневой роут с параметром, например: GET<a>http://localhost/1</a>.</p>
65 <p>Строка :id в декораторе @Get() означает, что в этот метод будут направлены запросы на корневой роут с параметром, например: GET<a>http://localhost/1</a>.</p>
66 <p>При помощи декоратора @Param() мы можем достать этот идентификатор из URL, преобразовать его к числу (ParseIntPipe) и использовать для поиска нужной статьи.</p>
66 <p>При помощи декоратора @Param() мы можем достать этот идентификатор из URL, преобразовать его к числу (ParseIntPipe) и использовать для поиска нужной статьи.</p>
67 <p><em>Кроме декоратора @Param() в Nest.js есть также декораторы @Query() для query-параметров и @Body() для тела запроса.</em></p>
67 <p><em>Кроме декоратора @Param() в Nest.js есть также декораторы @Query() для query-параметров и @Body() для тела запроса.</em></p>
68 <p>Добавим в папку<em>views</em>шаблон<em>article.pug</em>:</p>
68 <p>Добавим в папку<em>views</em>шаблон<em>article.pug</em>:</p>
69 <p>Шапка будет та же самая, только в тэге title мы выведем название статьи. В теле страницы - ожидаемо title и content, а также ссылка "назад" - на главную страницу.</p>
69 <p>Шапка будет та же самая, только в тэге title мы выведем название статьи. В теле страницы - ожидаемо title и content, а также ссылка "назад" - на главную страницу.</p>
70 <p>Осталось починить ссылки на главной странице:</p>
70 <p>Осталось починить ссылки на главной странице:</p>
71 <p>Теперь, зайдя на главную страницу, можно кликнуть по ссылке "Читать" у любой из статей и увидеть содержимое статьи. А при клике на "Назад" - вернуться к списку статей. Также можно зайти на страницу по прямой ссылке, например<a>http://localhost</a></p>
71 <p>Теперь, зайдя на главную страницу, можно кликнуть по ссылке "Читать" у любой из статей и увидеть содержимое статьи. А при клике на "Назад" - вернуться к списку статей. Также можно зайти на страницу по прямой ссылке, например<a>http://localhost</a></p>
72 <p><a>/1</a>.</p>
72 <p><a>/1</a>.</p>
73 <h2>Добавляем статью</h2>
73 <h2>Добавляем статью</h2>
74 <p>Чтобы создать новую статью, браузер должен отправить на сервер title и content POST-запросом. Чтобы реализовать это, добавим в контроллер перед методом article два новых метода:</p>
74 <p>Чтобы создать новую статью, браузер должен отправить на сервер title и content POST-запросом. Чтобы реализовать это, добавим в контроллер перед методом article два новых метода:</p>
75 <p>Метод getForm() не требует никакого возвращаемого значения - он просто возвращает статический HTML.</p>
75 <p>Метод getForm() не требует никакого возвращаемого значения - он просто возвращает статический HTML.</p>
76 <p>В методе create() реализуем добавление статьи в коллекцию. Думаю, читатель уже догадался, что декоратор @Post() означает одноимённый HTTP-глагол. Декоратор @Body() указывает фреймворку, что данные для параметра нужно брать из тела запроса. И, наконец, декоратор @Redirect(‘/’, 301) говорит, что после добавления статьи требуется переадресовать пользователя. Первый аргумент - ‘/’ обозначает корневой роут (то есть, главную страницу), а 301 - код ответа.</p>
76 <p>В методе create() реализуем добавление статьи в коллекцию. Думаю, читатель уже догадался, что декоратор @Post() означает одноимённый HTTP-глагол. Декоратор @Body() указывает фреймворку, что данные для параметра нужно брать из тела запроса. И, наконец, декоратор @Redirect(‘/’, 301) говорит, что после добавления статьи требуется переадресовать пользователя. Первый аргумент - ‘/’ обозначает корневой роут (то есть, главную страницу), а 301 - код ответа.</p>
77 <p>Шаблон<em>create-article.pug</em>будет выглядеть так:</p>
77 <p>Шаблон<em>create-article.pug</em>будет выглядеть так:</p>
78 <p>Добавление статей реализовано. На страницу добавления можно попасть, введя в адресной строке<a>http://localhost</a></p>
78 <p>Добавление статей реализовано. На страницу добавления можно попасть, введя в адресной строке<a>http://localhost</a></p>
79 <p><a>/create</a>. Чтобы было чуть удобнее, добавим ссылку на эту страничку вниз списка статей.<em>index.pug</em>приобретёт такой вид:</p>
79 <p><a>/create</a>. Чтобы было чуть удобнее, добавим ссылку на эту страничку вниз списка статей.<em>index.pug</em>приобретёт такой вид:</p>
80 <h2>Заключение</h2>
80 <h2>Заключение</h2>
81 <p>Nest.js позволяет нам написать простое приложение с минимальными усилиями. Однако истинная мощь фреймворка станет очевидна при более глубоком погружении. Среди особенно мощных возможностей, выделяющих его на фоне других:</p>
81 <p>Nest.js позволяет нам написать простое приложение с минимальными усилиями. Однако истинная мощь фреймворка станет очевидна при более глубоком погружении. Среди особенно мощных возможностей, выделяющих его на фоне других:</p>
82 <ul><li>Декларативное программирование при помощи декораторов</li>
82 <ul><li>Декларативное программирование при помощи декораторов</li>
83 <li>Встроенный DI-контейнер (<a>кратко о DI и IoC</a>)</li>
83 <li>Встроенный DI-контейнер (<a>кратко о DI и IoC</a>)</li>
84 <li>Мощные специализированные middleware:<a>интерцепторы</a>,<a>пайпы</a>,<a>гарды</a></li>
84 <li>Мощные специализированные middleware:<a>интерцепторы</a>,<a>пайпы</a>,<a>гарды</a></li>
85 <li>Механизм<a>обработки ошибок</a>"из коробки"</li>
85 <li>Механизм<a>обработки ошибок</a>"из коробки"</li>
86 <li>Поддержка любых протоколов, помимо HTTP. На Nest.js можно писать сервисы на основе RabbitMQ, Nats, Kafka или даже просто TCP-протокола.</li>
86 <li>Поддержка любых протоколов, помимо HTTP. На Nest.js можно писать сервисы на основе RabbitMQ, Nats, Kafka или даже просто TCP-протокола.</li>
87 </ul><p>Чтобы освоить все это, а также более продвинутые возможности Nest.js, читателю предстоит пройти немалый путь, и автор надеется, что эта статья станет уверенным первым шагом на нем.</p>
87 </ul><p>Чтобы освоить все это, а также более продвинутые возможности Nest.js, читателю предстоит пройти немалый путь, и автор надеется, что эта статья станет уверенным первым шагом на нем.</p>
88 <p><em>Весь код учебного проекта, который мы использовали в этой статье в качестве примера, вы можете посмотреть<a>здесь</a>.</em></p>
88 <p><em>Весь код учебного проекта, который мы использовали в этой статье в качестве примера, вы можете посмотреть<a>здесь</a>.</em></p>