Тема 7. Фреймворк NestJS. Контролери та маршрутизація

Встановлення NestJS CLI та створення проєкту

Інсталяція CLI, команда nest new, структура згенерованого проєкту

Встановлення NestJS CLI та створення проєкту

🎯 Мета лекції

  • Опанувати процес встановлення NestJS CLI та створення нового проєкту
  • Навчитися використовувати команди CLI для генерації базової структури застосунку
  • Розібрати структуру згенерованого проєкту та призначення кожного файлу
  • Зрозуміти конфігураційні файли TypeScript та NestJS
  • Навчитися запускати застосунок у режимі розробки та перевіряти його роботу

🔑 Ключові терміни

  • CLI (Command Line Interface): інтерфейс командного рядка для автоматизації типових завдань розробки
  • Package Manager (менеджер пакетів): інструмент для керування залежностями проєкту (npm, yarn, pnpm)
  • Bootstrap: процес ініціалізації та запуску застосунку
  • Entry Point (точка входу): файл, з якого починається виконання програми
  • Hot Reload (гаряче перезавантаження): автоматичне перезавантаження застосунку при зміні коду

NestJS CLI: інструмент професійної розробки

NestJS CLI (Command Line Interface) є потужним інструментом командного рядка, який значно спрощує та прискорює розробку застосунків на NestJS. CLI не є обов'язковим компонентом — технічно можна створювати NestJS-застосунки вручну, налаштовуючи всі файли самостійно. Проте використання CLI є загальноприйнятою практикою в індустрії, оскільки воно автоматизує рутинні завдання та забезпечує дотримання домовленостей фреймворку.

CLI виконує кілька критично важливих функцій у процесі розробки. По-перше, він надає команди для швидкого створення нових проєктів з правильно налаштованою базовою структурою. По-друге, CLI пропонує генератори (generators) для створення окремих компонентів застосунку — модулів, контролерів, сервісів, middleware та інших елементів архітектури. По-третє, CLI інтегрує інструменти для збірки проєкту, запуску в режимі розробки та підготовки до продакшн-середовища.

Важливо усвідомити, що NestJS CLI — це не просто набір скриптів для генерації шаблонного коду. Це інструмент, який втілює найкращі практики організації коду, дотримується архітектурних принципів фреймворку та забезпечує узгодженість структури проєктів різних команд і розробників. Використання CLI гарантує, що код буде відповідати стандартам спільноти NestJS та буде легко зрозумілим для будь-якого розробника, знайомого з фреймворком.

NestJS CLI побудовано на базі бібліотеки Schematics від Angular, що забезпечує потужну систему шаблонів та можливість створення власних генераторів коду. Це дозволяє великим командам адаптувати CLI під свої специфічні потреби, створюючи власні схеми генерації компонентів.

Встановлення NestJS CLI глобально

Перед створенням першого проєкту необхідно встановити NestJS CLI глобально на робочу машину. Глобальна інсталяція означає, що команда nest буде доступна з будь-якої директорії у терміналі, а не лише в контексті конкретного проєкту. Це дозволяє використовувати CLI для створення нових проєктів в будь-якому місці файлової системи.

NestJS CLI розповсюджується як npm-пакет під назвою @nestjs/cli. Його можна встановити за допомогою будь-якого з популярних менеджерів пакетів для Node.js екосистеми: npm (Node Package Manager), Yarn або pnpm. Вибір менеджера пакетів залежить від уподобань розробника та стандартів команди — всі три варіанти є рівноцінними для встановлення CLI.

npm install -g @nestjs/cli

Команда -g (або --global) вказує npm встановити пакет глобально, а не локально в поточний проєкт. Після успішної інсталяції команда nest стане доступною в терміналі.

Процес встановлення може зайняти від кількох секунд до хвилини залежно від швидкості інтернет-з'єднання. CLI завантажить власний пакет та всі його транзитивні залежності (transitive dependencies), які необхідні для роботи генераторів коду та інших функцій інструменту.

Якщо під час встановлення виникають помилки, пов'язані з правами доступу (особливо на Unix-подібних системах), можна або налаштувати npm для використання директорії користувача замість системної, або скористатися менеджером версій Node.js, таким як nvm (Node Version Manager), який автоматично керує правами доступу.

Перевірка встановлення та версії CLI

Після завершення інсталяції рекомендується перевірити, що NestJS CLI встановлено коректно та доступно в системі. Це можна зробити за допомогою команди перевірки версії:

bash
$ nest --version
11.0.2

Команда nest --version (або скорочено nest -v) виводить поточну версію встановленого CLI. Номер версії може відрізнятися залежно від дати встановлення, оскільки NestJS активно розвивається та регулярно випускає нові релізи. На момент написання цього матеріалу актуальною є версія 11.x, проте всі базові концепції та команди залишаються сумісними між версіями.

Якщо команда виконується успішно і виводить номер версії, це означає, що CLI встановлено коректно і готовий до використання. Якщо ж система повідомляє, що команда nest не знайдена (command not found), можливі наступні причини:

  1. Глобальна інсталяція пакету не завершилася успішно
  2. Шлях до глобальних npm-пакетів не додано до системної змінної PATH
  3. Після встановлення необхідно перезапустити термінал для оновлення змінних оточення
На деяких корпоративних машинах з обмеженими правами доступу глобальна інсталяція пакетів може бути заборонена політиками безпеки. У таких випадках можна використовувати npx @nestjs/cli для виконання команд CLI без глобального встановлення, проте це менш зручно для регулярного використання.

Довідкова інформація CLI

NestJS CLI пропонує вбудовану систему довідки, яка допомагає розробникам швидко знайти потрібну команду або параметр без необхідності звертатися до зовнішньої документації. Для отримання загального огляду доступних команд використовується:

bash
$ nest --help
Usage: nest [command] [options]
Commands:
new|n [options] [name] Generate Nest application
generate|g [options] [schematic] Generate Nest element
build [options] [app] Build Nest application
start [options] [app] Run Nest application
info|i Display Nest project details

Кожна команда має власний набір опцій та параметрів, детальну інформацію про які можна отримати, додавши --help після назви команди. Наприклад, nest new --help виведе всі доступні параметри для команди створення нового проєкту.

Створення нового проєкту: команда nest new

Команда nest new є відправною точкою для будь-якого NestJS-проєкту. Вона створює повноцінну структуру застосунку з усіма необхідними файлами, конфігурацією та початковим кодом. Ця команда виконує значно більше, ніж просто копіювання шаблонних файлів — вона налаштовує TypeScript-компілятор, ініціалізує систему контролю версій, встановлює залежності та готує проєкт до негайної розробки.

Синтаксис команди та параметри

Базовий синтаксис команди nest new виглядає наступним чином:

nest new <project-name> [options]

Де <project-name> — це назва директорії, в якій буде створено проєкт, та одночасно назва застосунку в метаданих пакету. Назва має відповідати правилам іменування npm-пакетів: малі літери, без пробілів (замість них використовуються дефіси або нижні підкреслення).

CLI пропонує кілька корисних опцій для налаштування процесу створення проєкту:

  • --dry-run або -d: виконує команду в тестовому режимі без фактичного створення файлів, показуючи лише що буде створено
  • --skip-git або -g: пропускає ініціалізацію Git-репозиторію
  • --skip-install або -s: пропускає автоматичне встановлення залежностей через npm/yarn/pnpm
  • --package-manager <manager>: явно вказує який менеджер пакетів використовувати (npm, yarn, pnpm)
  • --language <language>: вибір мови програмування (TypeScript або JavaScript, за замовчуванням TypeScript)
  • --strict: увімкнення строгого режиму TypeScript з усіма перевірками типів
Опція --strict активує найсуворіші правила перевірки типів TypeScript, включаючи strictNullChecks, noImplicitAny, strictBindCallApply та інші. Це рекомендований підхід для нових проєктів, оскільки він максимально використовує переваги статичної типізації та допомагає виявляти потенційні помилки на етапі компіляції.

Інтерактивне створення проєкту

Практичний процес створення нового проєкту починається з виконання команди в терміналі. Припустимо, потрібно створити застосунок для керування задачами з назвою task-manager:

bash
$ nest new task-manager
⚡ We will scaffold your app in a few seconds...
? Which package manager would you ❤️ to use?
❯ npm
yarn
pnpm

Після введення команди CLI запитує, який менеджер пакетів використовувати для встановлення залежностей. Цей вибір впливає на те, який файл lock буде створено (package-lock.json для npm, yarn.lock для Yarn, pnpm-lock.yaml для pnpm) та які команди використовуватимуться для керування залежностями надалі.

Вибір менеджера пакетів здійснюється за допомогою стрілок на клавіатурі та підтвердження клавішею Enter. Якщо потрібно автоматизувати процес і уникнути інтерактивного діалогу, можна явно вказати менеджер через параметр:

nest new task-manager --package-manager npm

Після вибору менеджера пакетів CLI виконує кілька послідовних операцій:

  1. Створення структури директорій: Формується базова організація файлів та папок проєкту
  2. Генерація початкового коду: Створюються файли з базовою реалізацією модуля, контролера та сервісу
  3. Копіювання конфігураційних файлів: Розміщуються файли налаштувань для TypeScript, NestJS CLI, ESLint, Prettier та інших інструментів
  4. Ініціалізація Git-репозиторію: Створюється локальний Git-репозиторій з початковим комітом (якщо не використано --skip-git)
  5. Встановлення залежностей: Завантажуються та інсталюються всі npm-пакети, необхідні для роботи застосунку
bash
CREATE task-manager/.eslintrc.js (663 bytes)
CREATE task-manager/.prettierrc (51 bytes)
CREATE task-manager/README.md (3339 bytes)
CREATE task-manager/nest-cli.json (171 bytes)
CREATE task-manager/package.json (1948 bytes)
CREATE task-manager/tsconfig.build.json (97 bytes)
CREATE task-manager/tsconfig.json (546 bytes)
CREATE task-manager/src/app.controller.spec.ts (617 bytes)
CREATE task-manager/src/app.controller.ts (274 bytes)
CREATE task-manager/src/app.module.ts (249 bytes)
CREATE task-manager/src/app.service.ts (142 bytes)
CREATE task-manager/src/main.ts (208 bytes)
CREATE task-manager/test/app.e2e-spec.ts (630 bytes)
CREATE task-manager/test/jest-e2e.json (183 bytes)
📦 Installing packages... This might take a couple of minutes.

Встановлення залежностей зазвичай є найдовшим етапом процесу створення проєкту, оскільки потрібно завантажити десятки пакетів, включаючи ядро NestJS, підтримку TypeScript, фреймворк тестування Jest, лінтери та інші інструменти розробки. Час виконання залежить від швидкості інтернет-з'єднання та продуктивності системи.

Якщо процес створення проєкту перервався через помилку мережі під час встановлення залежностей, можна вручну завершити інсталяцію, перейшовши в директорію проєкту та виконавши команду встановлення відповідного менеджера пакетів (npm install, yarn або pnpm install).

Після успішного завершення всіх операцій CLI виводить повідомлення про готовність проєкту:

bash
✔ Installation in progress... ☕
✔ Packages installed successfully.
🚀 Successfully created project task-manager
👉 Get started with the following commands:
$ cd task-manager
$ npm run start

Структура згенерованого проєкту: огляд архітектури

Після створення проєкту в директорії task-manager з'являється добре структурована організація файлів та папок, кожен елемент якої має чітко визначене призначення. Розуміння цієї структури є критично важливим для ефективної роботи з NestJS, оскільки воно визначає, де розміщувати новий код та як організовувати застосунок у міру його зростання.

Кореневі файли конфігурації

У кореневій директорії проєкту розміщено набір конфігураційних файлів, які керують поведінкою різних інструментів розробки:

task-manager/
├── .eslintrc.js          # Конфігурація ESLint для статичного аналізу коду
├── .prettierrc           # Налаштування форматування коду
├── .gitignore            # Список файлів, ігнорованих Git
├── nest-cli.json         # Конфігурація NestJS CLI
├── package.json          # Метадані проєкту та список залежностей
├── tsconfig.json         # Основна конфігурація TypeScript
├── tsconfig.build.json   # Конфігурація TypeScript для продакшн-збірки
├── README.md             # Документація проєкту
└── ...

Файл package.json є центральним маніфестом Node.js проєкту. Він містить метадані застосунку (назва, версія, опис), списки залежностей (dependencies та devDependencies) та скрипти для виконання типових завдань розробки:

{
  "name": "task-manager",
  "version": "0.0.1",
  "description": "",
  "author": "",
  "private": true,
  "license": "UNLICENSED",
  "scripts": {
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start",
    "start:dev": "nest start --watch",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  },
  "dependencies": {
    "@nestjs/common": "^11.0.0",
    "@nestjs/core": "^11.0.0",
    "@nestjs/platform-express": "^11.0.0",
    "reflect-metadata": "^0.2.0",
    "rxjs": "^7.8.1"
  },
  "devDependencies": {
    "@nestjs/cli": "^11.0.0",
    "@nestjs/schematics": "^11.0.0",
    "@nestjs/testing": "^11.0.0",
    "@types/express": "^4.17.17",
    "@types/jest": "^29.5.2",
    "@types/node": "^20.3.1",
    "@types/supertest": "^6.0.0",
    "@typescript-eslint/eslint-plugin": "^8.0.0",
    "@typescript-eslint/parser": "^8.0.0",
    "eslint": "^8.42.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-prettier": "^5.0.0",
    "jest": "^29.5.0",
    "prettier": "^3.0.0",
    "source-map-support": "^0.5.21",
    "supertest": "^7.0.0",
    "ts-jest": "^29.1.0",
    "ts-loader": "^9.4.3",
    "ts-node": "^10.9.1",
    "tsconfig-paths": "^4.2.0",
    "typescript": "^5.1.3"
  }
}

Секція scripts визначає команди, які можна виконувати через менеджер пакетів. Наприклад, npm run start:dev запускає застосунок у режимі розробки з автоматичним перезавантаженням при зміні файлів.

Залежності розділені на дві категорії: dependencies містить пакети, необхідні для роботи застосунку в продакшні (ядро NestJS, HTTP-адаптер Express, утиліти), а devDependencies містить інструменти розробки (компілятор TypeScript, фреймворк тестування, лінтери), які не потрібні в продакшн-середовищі.

Файл tsconfig.json налаштовує поведінку компілятора TypeScript, визначаючи рівень строгості перевірки типів, версію JavaScript для генерації коду, систему модулів та інші параметри компіляції:

{
  "compilerOptions": {
    "module": "commonjs",
    "declaration": true,
    "removeComments": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "target": "ES2021",
    "sourceMap": true,
    "outDir": "./dist",
    "baseUrl": "./",
    "incremental": true,
    "skipLibCheck": true,
    "strictNullChecks": false,
    "noImplicitAny": false,
    "strictBindCallApply": false,
    "forceConsistentCasingInFileNames": false,
    "noFallthroughCasesInSwitch": false
  }
}

Ключові параметри, специфічні для NestJS:

  • experimentalDecorators: true: Увімкнення підтримки декораторів TypeScript, які є фундаментом синтаксису NestJS
  • emitDecoratorMetadata: true: Генерація метаданих про типи для декораторів, що необхідно для роботи системи впровадження залежностей
  • module: "commonjs": Використання системи модулів CommonJS, сумісної з Node.js
  • target: "ES2021": Генерація коду JavaScript версії ES2021
  • outDir: "./dist": Директорія для скомпільованого JavaScript-коду

Файл nest-cli.json містить налаштування специфічні для CLI NestJS:

{
  "$schema": "https://json.schemastore.org/nest-cli",
  "collection": "@nestjs/schematics",
  "sourceRoot": "src",
  "compilerOptions": {
    "deleteOutDir": true,
    "webpack": false
  }
}

Цей файл визначає, яка колекція схем використовується для генерації коду (@nestjs/schematics), де розміщено вихідний код (sourceRoot: "src") та додаткові опції компілятора.

Директорія src: вихідний код застосунку

Директорія src/ містить весь вихідний код TypeScript застосунку. У базовому проєкті CLI генерує чотири файли, які утворюють мінімально функціональний NestJS-застосунок:

src/
├── main.ts                # Точка входу: ініціалізація та запуск застосунку
├── app.module.ts          # Кореневий модуль: організаційна одиниця застосунку
├── app.controller.ts      # Контролер: обробка HTTP-запитів
├── app.service.ts         # Сервіс: бізнес-логіка
└── app.controller.spec.ts # Модульний тест для контролера

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

Файл main.ts: точка входу та bootstrap

Файл main.ts є точкою входу (entry point) застосунку — саме з нього починається виконання програми. Цей файл містить асинхронну функцію bootstrap(), яка виконує ініціалізацію NestJS-застосунку та запускає HTTP-сервер:

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

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

Розберемо кожен рядок цього коду детально:

Рядок 1: Імпорт NestFactory

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

NestFactory — це фабричний клас (factory class) з пакету @nestjs/core, який відповідає за створення екземпляра NestJS-застосунку. Фабричний патерн інкапсулює складну логіку ініціалізації IoC-контейнера, налаштування HTTP-адаптера та підготовку всієї інфраструктури фреймворку.

Рядок 2: Імпорт кореневого модуля

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

AppModule — це кореневий модуль застосунку, який служить вхідною точкою для системи модулів NestJS. Саме через цей модуль фреймворк дізнається про всі інші модулі, контролери та провайдери застосунку.

Рядки 4-7: Функція bootstrap

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

Функція bootstrap() оголошена як асинхронна (async), оскільки процес ініціалізації застосунку включає асинхронні операції. Метод NestFactory.create() повертає Promise, який розв'язується у екземпляр INestApplication — об'єкт, що представляє запущений застосунок.

Метод app.listen(3000) запускає HTTP-сервер на порту 3000. Після виклику цього методу застосунок починає приймати вхідні HTTP-запити. Номер порту можна легко параметризувати через змінні оточення:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  const port = process.env.PORT || 3000;
  await app.listen(port);
  console.log(`Application is running on: ${await app.getUrl()}`);
}
bootstrap();
У продакшн-середовищі рекомендується використовувати змінні оточення для всіх конфігураційних параметрів, включаючи порт, рядок підключення до бази даних, ключі API тощо. Це дозволяє розгортати один і той самий код у різних середовищах без необхідності його зміни.

Функція bootstrap() викликається одразу після свого оголошення, ініціюючи процес запуску застосунку. Якщо під час ініціалізації виникає помилка (наприклад, порт зайнятий іншим процесом), Promise буде відхилено і Node.js виведе інформацію про помилку в консоль та завершить процес.

Файл app.module.ts: кореневий модуль

Файл app.module.ts визначає кореневий модуль застосунку — організаційну одиницю, яка об'єднує всі інші компоненти системи:

import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Цей код демонструє базову структуру модуля NestJS:

Декоратор @Module: Позначає клас як модуль та приймає об'єкт конфігурації з метаданими:

  • imports: []: Масив інших модулів, функціональність яких потрібна поточному модулю. У базовому застосунку він порожній, оскільки AppModule є єдиним модулем і не залежить від інших
  • controllers: [AppController]: Масив контролерів, що належать цьому модулю. NestJS автоматично створить екземпляр AppController та зареєструє його маршрути
  • providers: [AppService]: Масив провайдерів (сервісів), доступних для впровадження в межах модуля. AppService буде керуватися IoC-контейнером та може бути впроваджений у конструктор AppController

Клас AppModule: Сам клас модуля є порожнім — він не містить методів чи властивостей. Вся конфігурація визначається через декоратор @Module(). Це типовий патерн для модулів NestJS — метадані, а не код класу, визначають поведінку.

У міру зростання застосунку AppModule зазвичай не містить власної функціональності, а слугує точкою інтеграції, імпортуючи інші feature-модулі. Наприклад, у реальному застосунку масив imports міг би містити UsersModule, AuthModule, ProductsModule тощо.

Файл app.controller.ts: обробка запитів

Контролер app.controller.ts демонструє базову обробку HTTP-запитів:

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();
  }
}

Розберемо структуру контролера:

Декоратор @Controller(): Позначає клас як контролер та опціонально приймає префікс шляху. У цьому випадку декоратор викликано без аргументів, що означає, що маршрути цього контролера будуть доступні від кореня застосунку (/).

Впровадження залежностей через конструктор:

constructor(private readonly appService: AppService) {}

У конструкторі оголошено параметр appService з модифікатором private readonly. Це скорочений синтаксис TypeScript, який одночасно:

  1. Оголошує приватну властивість класу appService
  2. Автоматично присвоює значення параметра конструктора цій властивості
  3. Позначає властивість як readonly, що забороняє її зміну після ініціалізації

NestJS бачить, що конструктор вимагає екземпляр типу AppService, знаходить відповідний провайдер у модулі та автоматично впроваджує його при створенні контролера.

Декоратор @Get() та метод-обробник:

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

Декоратор @Get() позначає метод як обробник GET-запитів. Без аргументів він обробляє запити до базового шляху контролера. Метод викликає сервіс для отримання даних та повертає результат. NestJS автоматично серіалізує повернене значення (у даному випадку рядок) у HTTP-відповідь.

Файл app.service.ts: бізнес-логіка

Сервіс app.service.ts інкапсулює бізнес-логіку застосунку:

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

@Injectable()
export class AppService {
  getHello(): string {
    return 'Hello World!';
  }
}

Декоратор @Injectable(): Позначає клас як провайдер, який може бути впроваджений у інші класи через систему Dependency Injection. Без цього декоратора NestJS не зможе керувати життєвим циклом класу та впроваджувати його як залежність.

Метод getHello(): Містить тривіальну бізнес-логіку — повертає рядок "Hello World!". У реальному застосунку сервіси містять значно складнішу логіку: обробку бізнес-правил, взаємодію з базою даних, виклики зовнішніх API, трансформацію даних тощо.

Директорія test: інтеграційні тести

Окремо від модульних тестів (файли *.spec.ts поряд з кодом) CLI створює директорію test/ для end-to-end (E2E) тестів:

test/
├── app.e2e-spec.ts    # E2E тест базової функціональності
└── jest-e2e.json      # Конфігурація Jest для E2E тестів

E2E тести перевіряють роботу всього застосунку «від краю до краю», імітуючи реальні HTTP-запити до сервера:

import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';

describe('AppController (e2e)', () => {
  let app: INestApplication;

  beforeEach(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

  it('/ (GET)', () => {
    return request(app.getHttpServer())
      .get('/')
      .expect(200)
      .expect('Hello World!');
  });
});

Цей тест створює повноцінний екземпляр застосунку, використовуючи бібліотеку supertest для виконання HTTP-запитів, та перевіряє, що GET-запит до кореневого шляху повертає статус 200 та текст "Hello World!".

Запуск застосунку: режими розробки та продакшн

Після створення проєкту та ознайомлення з його структурою настає час запустити застосунок та переконатися, що він працює коректно. NestJS пропонує кілька режимів запуску, кожен з яких оптимізований для певного етапу розробки.

Режим розробки з hot reload

Для повсякденної розробки використовується команда start:dev, яка запускає застосунок у режимі спостереження (watch mode) з автоматичним перезавантаженням при зміні файлів:

bash
$ cd task-manager
$ npm run start:dev
[Nest] 47821 - 12/03/2026, 10:23:15 AM LOG [NestFactory] Starting Nest application...
[Nest] 47821 - 12/03/2026, 10:23:15 AM LOG [InstanceLoader] AppModule dependencies initialized +18ms
[Nest] 47821 - 12/03/2026, 10:23:15 AM LOG [RoutesResolver] AppController {/}: +4ms
[Nest] 47821 - 12/03/2026, 10:23:15 AM LOG [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 47821 - 12/03/2026, 10:23:15 AM LOG [NestApplication] Nest application successfully started +3ms

Логи показують процес ініціалізації:

  1. Starting Nest application: Початок процесу bootstrap
  2. AppModule dependencies initialized: IoC-контейнер розв'язав всі залежності модуля
  3. RoutesResolver: Система знайшла контролери та їхні маршрути
  4. Mapped {/, GET} route: Маршрут зареєстровано в системі маршрутизації
  5. Nest application successfully started: Застосунок готовий приймати запити

Числа з жовтим плюсом (наприклад, +18ms) показують час виконання кожного етапу відносно попереднього. Це корисно для діагностики проблем з продуктивністю при запуску.

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

Після успішного запуску застосунок слухає на порту 3000. Перевірити його роботу можна кількома способами:

Через браузер: Відкрити адресу http://localhost:3000 у веб-браузері. Має з'явитися текст "Hello World!".

Через curl: Виконати HTTP-запит із командного рядка:

bash
$ curl http://localhost:3000
Hello World!

Через інструменти розробника API: Використати Postman, Insomnia або Thunder Client для відправки GET-запиту на http://localhost:3000.

Під час розробки рекомендується тримати термінал із запущеним npm run start:dev постійно відкритим. Це дозволяє миттєво бачити логи застосунку, помилки компіляції TypeScript та результати автоматичного перезавантаження після збереження файлів.

Гаряче перезавантаження в дії

Механізм hot reload є однією з найбільш корисних можливостей режиму розробки. Спробуємо змінити код та побачити, як застосунок автоматично перезавантажується:

Відредагуйте файл src/app.service.ts:

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

@Injectable()
export class AppService {
  getHello(): string {
    return 'Привіт від NestJS! 🚀'; // Змінено повідомлення
  }
}

Одразу після збереження файлу в терміналі з'являться нові логи:

bash
[Nest] 47821 - 12/03/2026, 10:25:42 AM LOG File change detected. Starting incremental compilation...
Found 0 errors. Watching for file changes.
[Nest] 47821 - 12/03/2026, 10:25:43 AM LOG [NestFactory] Starting Nest application...
[Nest] 47821 - 12/03/2026, 10:25:43 AM LOG [InstanceLoader] AppModule dependencies initialized +12ms
[Nest] 47821 - 12/03/2026, 10:25:43 AM LOG [NestApplication] Nest application successfully started +2ms

Застосунок автоматично перекомпілював змінені файли TypeScript та перезапустився. Тепер GET-запит до http://localhost:3000 поверне нове повідомлення без необхідності вручну зупиняти та перезапускати сервер.

Режим відлагодження

Для складніших випадків, коли потрібно використовувати точки зупинки (breakpoints) та покроковий дебагер, доступний режим debug:

npm run start:debug

Ця команда запускає Node.js у режимі інспектування, дозволяючи підключити дебагер з IDE (VS Code, WebStorm) або Chrome DevTools для покрокового виконання коду та аналізу стану програми.

Продакшн-збірка та запуск

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

# Компіляція TypeScript у оптимізований JavaScript
npm run build

# Запуск скомпільованого коду
npm run start:prod

Команда build виконує повну компіляцію проєкту, генеруючи JavaScript-файли в директорії dist/. Продакшн-збірка відрізняється від режиму розробки:

  • Відсутні source maps (якщо не налаштовано інакше)
  • Код оптимізовано для продуктивності
  • Видалено коментарі та непотрібні метадані
  • Застосовано tree-shaking для видалення невикористаного коду
Ніколи не використовуйте режим розробки (start:dev) на продакшн-серверах. Режим watch постійно слідкує за змінами файлів, що споживає ресурси, а TypeScript компілюється «на льоту» без оптимізацій. Завжди виконуйте npm run build та запускайте скомпільований код через start:prod.

Налаштування конфігурації: адаптація під потреби

Хоча згенерований проєкт готовий до роботи «з коробки», реальні застосунки часто потребують додаткових налаштувань. Розглянемо типові сценарії кастомізації конфігурації.

Зміна порту через змінні оточення

Жорстке кодування порту в main.ts не є гнучким рішенням. Професійний підхід передбачає використання змінних оточення (environment variables):

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // Використання змінної оточення PORT або значення за замовчуванням
  const port = process.env.PORT || 3000;
  
  await app.listen(port);
  console.log(`🚀 Application is running on: http://localhost:${port}`);
}
bootstrap();

Тепер порт можна змінювати без модифікації коду:

bash
$ PORT=4000 npm run start:dev
[Nest] 48123 - 12/03/2026, 10:30:00 AM LOG [NestApplication] Nest application successfully started
🚀 Application is running on: http://localhost:4000

Увімкнення CORS для фронтенд-інтеграції

Якщо планується розробка SPA-фронтенду (React, Vue, Angular), який працює на іншому порту під час розробки, необхідно увімкнути CORS (Cross-Origin Resource Sharing):

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // Увімкнення CORS для дозволу запитів з інших доменів
  app.enableCors({
    origin: 'http://localhost:5173', // Адреса фронтенду (наприклад, Vite)
    credentials: true,
  });
  
  await app.listen(3000);
}
bootstrap();

Для розробки можна дозволити всі джерела:

app.enableCors(); // Дозволяє запити з будь-яких доменів
У продакшн-середовищі завжди явно вказуйте дозволені домени в конфігурації CORS. Дозвіл усіх джерел (origin: '*') створює ризики безпеки, дозволяючи будь-якому вебсайту виконувати запити до вашого API від імені користувачів.

Налаштування глобального префіксу API

Багато застосунків використовують версіювання API через URL-префікси (/api/v1, /api/v2). NestJS дозволяє встановити глобальний префікс для всіх маршрутів:

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // Всі маршрути тепер починатимуться з /api
  app.setGlobalPrefix('api');
  
  await app.listen(3000);
}
bootstrap();

Після цієї зміни маршрут, який раніше був доступний за адресою http://localhost:3000/, тепер буде доступний за адресою http://localhost:3000/api/.

Для версіювання:

app.setGlobalPrefix('api/v1');

Тепер усі маршрути автоматично отримують префікс /api/v1.

Увімкнення строгого режиму TypeScript

Якщо проєкт створено без опції --strict, але пізніше вирішено увімкнути суворі перевірки типів, необхідно модифікувати tsconfig.json:

{
  "compilerOptions": {
    "strict": true,
    "strictNullChecks": true,
    "noImplicitAny": true,
    "strictBindCallApply": true,
    "forceConsistentCasingInFileNames": true,
    "noFallthroughCasesInSwitch": true,
    // ... інші опції
  }
}

Увімкнення цих опцій може виявити приховані помилки в коді, проте вимагатиме додаткових анотацій типів та обробки null/undefined значень.

Типові проблеми та їх вирішення

При першому знайомстві з NestJS розробники можуть зіткнутися з кількома типовими проблемами. Розглянемо найпоширеніші з них.

Помилка: "Cannot find module '@nestjs/core'"

Ця помилка вказує, що залежності не встановлено коректно. Рішення:

# Видалити node_modules та lock-файли
rm -rf node_modules package-lock.json

# Перевстановити залежності
npm install

Помилка: "Address already in use"

Якщо порт 3000 вже використовується іншим застосунком:

# Знайти процес, що використовує порт 3000 (macOS/Linux)
lsof -i :3000

# Завершити процес (замінити PID на фактичний)
kill -9 <PID>

# Або змінити порт у main.ts або через змінну оточення
PORT=4000 npm run start:dev

Помилка: "Nest can't resolve dependencies"

Ця помилка виникає, коли IoC-контейнер не може знайти провайдер для впровадження. Найчастіші причини:

  1. Забули додати @Injectable() до класу провайдера
  2. Провайдер не зареєстровано в масиві providers модуля
  3. Провайдер належить до іншого модуля, який не імпортовано

Рішення — перевірити, що всі залежності коректно оголошені та зареєстровані.

Практичне завдання: розширення базового застосунку

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

Крок 1: Модифікація існуючого ендпоінту

Змініть app.service.ts, щоб метод getHello() повертав об'єкт замість рядка:

getHello(): object {
  return {
    message: 'Вітаємо в NestJS!',
    version: '1.0.0',
    timestamp: new Date().toISOString(),
  };
}

Перевірте, що тепер GET-запит до http://localhost:3000 повертає JSON-об'єкт.

Крок 2: Додавання нового маршруту

Додайте новий метод у app.controller.ts:

@Get('info')
getInfo(): object {
  return {
    app: 'Task Manager API',
    environment: process.env.NODE_ENV || 'development',
  };
}

Перевірте, що запит до http://localhost:3000/info повертає інформацію про застосунок.

Крок 3: Налаштування глобального префіксу

Додайте глобальний префікс api в main.ts та перевірте, що маршрути тепер доступні за адресами http://localhost:3000/api/ та http://localhost:3000/api/info.

Підсумок: перший крок у світ NestJS

У цій лекції ми опанували фундаментальні навички роботи з NestJS CLI та створення базового застосунку. Ми встановили CLI глобально, створили новий проєкт, детально розібрали структуру згенерованих файлів та навчилися запускати застосунок у режимі розробки з автоматичним перезавантаженням.

Ключові концепції, з якими ми познайомилися:

  • NestJS CLI — потужний інструмент автоматизації, що забезпечує узгодженість структури проєктів та прискорює розробку
  • Точка входу (main.ts) — файл, що виконує bootstrap застосунку через NestFactory.create() та запускає HTTP-сервер
  • Кореневий модуль (AppModule) — організаційна одиниця, що об'єднує контролери та провайдери застосунку
  • Розділення відповідальностей — контролери обробляють HTTP-запити, а сервіси інкапсулюють бізнес-логіку
  • Впровадження залежностей — автоматичне керування створенням та впровадженням екземплярів класів через IoC-контейнер

Структура, згенерована CLI, є відправною точкою для будь-якого NestJS-проєкту. У наступних лекціях ми детальніше розглянемо кожен компонент цієї архітектури, навчимося створювати складніші маршрути, працювати з базами даних, реалізовувати автентифікацію та будувати повноцінні RESTful API.

Зберігайте базовий проєкт, створений у цій лекції, як еталон структури. У міру вивчення нових концепцій NestJS ви зможете експериментувати з кодом, а у разі помилок — швидко повернутися до чистої початкової структури, створивши новий проєкт командою nest new.

✅ Що ми опанували

  • Встановлення та використання NestJS CLI
  • Створення нового проєкту з правильною структурою
  • Розуміння призначення кожного згенерованого файлу
  • Запуск застосунку в режимі розробки та продакшн
  • Базова конфігурація застосунку (порт, CORS, префікси)
  • Розв'язання типових проблем при встановленні

🎯 Наступні кроки

У наступній лекції ми детальніше розглянемо структуру проєкту NestJS та конвенції організації коду. Ми навчимося створювати feature-модулі, організовувати код за доменами та дотримуватися найкращих практик структурування великих застосунків.
Copyright © 2026