Гид по технологиям

Построение CRUD REST API на Nest.js с TypeORM и PostgreSQL

7 min read Backend Обновлено 29 Dec 2025
Nest.js CRUD с TypeORM и PostgreSQL
Nest.js CRUD с TypeORM и PostgreSQL

Редактор с JavaScript-кодом на фоне и логотипом Nest.js на переднем плане.

Зачем использовать Nest.js для CRUD API

Nest.js — это прогрессивный фреймворк для Node.js, который строится поверх Express (или Fastify) и приносит модульность, инверсию зависимостей и структурированную архитектуру. Для CRUD API Nest.js помогает:

  • Снизить беспорядок в коде благодаря модулям и инъекции зависимостей.
  • Быстро интегрировать ORM (TypeORM, Sequelize, Prisma) для работы с базой данных.
  • Упрощать масштабирование через разделение на модули и контроллеры.

Краткое определение: CRUD — набор операций Create, Read, Update, Delete, необходимых для работы с любыми ресурсами в API.

Основные варианты задач, решаемых этой статьёй

primary intent: создать CRUD REST API на Nest.js related variants: Nest.js CRUD, TypeORM PostgreSQL, Nest.js + TypeORM, Nest CRUD пример, бекенд на Node.js с PostgreSQL

Требования и оговорки

Важно: в примерах используется PostgreSQL. Для локальной разработки можно использовать Docker или облачный экземпляр (например, ElephantSQL). По умолчанию приложение запускается на порту 3000, а PostgreSQL слушает порт 5432.

Примечание: в примерах для простоты включена опция synchronize: true в TypeORM — это удобно для разработки, но не рекомендуется в production, так как может привести к неявным изменениям схемы.

Шаг 1. Установка Nest CLI и создание проекта

Установите Nest CLI глобально и создайте проект:

npm i -g @nestjs/cli
nest new crud-app
cd crud-app
npm run start

CLI предложит выбрать пакетный менеджер. В примерах используется npm.

Важно: при первом запуске dev-сервер запустится на http://localhost:3000.

Шаг 2. Создание PostgreSQL базы данных

Для облачного решения можно использовать ElephantSQL:

  1. Перейдите на сайт ElephantSQL и авторизуйтесь.

Домашняя страница ElephantSQL с формой входа и навигацией.

  1. Нажмите Create New Instance в верхней части страницы.

Кнопка Create New Instance на платформе ElephantSQL для создания PostgreSQL-экземпляра.

  1. Заполните имя инстанса, выберите бесплатный план и регион. После создания скопируйте DATABASE URL в разделе настроек.

Совет: для локальной разработки проще поднять PostgreSQL через Docker:

docker run --name pg-dev -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres -p 5432:5432 -d postgres:14

Шаг 3. Конфигурация подключения к БД

Создайте в корне проекта файл .env и вставьте туда строку подключения:

DATABASE_URL=""

Установите зависимости:

npm install pg typeorm @nestjs/typeorm @nestjs/config

Создайте модуль базы данных:

nest g module database

Откройте файл database/database.module.ts и добавьте конфигурацию TypeORM (пример):

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from '../users/models/user.entity';

@Module({
  imports: [
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: async (configService: ConfigService) => ({
        type: 'postgres',
        url: configService.get('DATABASE_URL'),
        entities: [User],
        synchronize: true
      }),
    }),
  ],
})
export class DatabaseModule {}

Пояснение: TypeOrmModule.forRootAsync позволяет считывать переменные окружения через ConfigService.

Обновите app.module.ts, чтобы подключить ConfigModule и DatabaseModule:

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { DatabaseModule } from './database/database.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      envFilePath: '.env',
    }),
    DatabaseModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Важно: не коммитите .env в репозиторий. Для production используйте безопасное хранилище секретов.

Шаг 4. Определение сущности User

Создайте файл users/models/user.entity.ts с определением сущности:

import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;
}

Пояснение: декораторы TypeORM описывают схему таблицы — удобный подход для миграций и валидации типов на уровне TypeScript.

Шаг 5. Реализация сервиса CRUD

Создайте сервис пользователей:

nest g service users

Пример users.service.ts:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './models/user.entity';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository,
  ) {}

  async findAll(): Promise {
    return this.userRepository.find();
  }

  async findOne(id: number): Promise {
    return this.userRepository.findOne({ where: { id } });
  }

  async create(user: Partial): Promise {
    const newuser = this.userRepository.create(user);
    return this.userRepository.save(newuser);
  }

  async update(id: number, user: Partial): Promise {
    await this.userRepository.update(id, user);
    return this.userRepository.findOne({ where: { id } });
  }

  async delete(id: number): Promise {
    await this.userRepository.delete(id);
  }
}

Примечание: здесь используется Partial для гибкости — это полезно при частичных обновлениях.

Шаг 6. Определение контроллера API

Создайте контроллер командой:

nest g controller users

Пример users.controller.ts:

import { Controller, Get, Post, Body, Put, Param, Delete, NotFoundException, HttpCode } from '@nestjs/common';
import { UsersService } from './users.service';
import { User } from './models/user.entity';

@Controller('api/users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}

  @Get()
  async findAll(): Promise {
    return this.usersService.findAll();
  }

  @Post()
  @HttpCode(201)
  async create(@Body() user: User): Promise {
    const createdUser = await this.usersService.create(user);
    return createdUser;
  }

  @Put(':id')
  async update(@Param('id') id: number, @Body() user: User): Promise {
    await this.usersService.update(id, user);
    return { message: 'User updated successfully' };
  }

  @Delete(':id')
  async delete(@Param('id') id: number): Promise {
    const user = await this.usersService.findOne(id);

    if (!user) {
      throw new NotFoundException('User does not exist!');
    }

    await this.usersService.delete(id);
    return { message: 'User deleted successfully' };
  }
}

Пояснение: контроллер обрабатывает HTTP-запросы и делегирует логику сервису. Для production добавьте валидацию DTO и фильтры ошибок.

Шаг 7. Подключение модуля пользователей

Обновите users.module.ts:

import { Module } from '@nestjs/common';
import { UsersController } from './users.controller';
import { UsersService } from './users.service';
import { TypeOrmModule } from '@nestjs/typeorm';
import { User } from './models/user.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User])],
  controllers: [UsersController],
  providers: [UsersService]
})
export class UsersModule {}

Запустите сервер:

npm run start

API будет доступно по адресу: http://localhost:3000/api/users

Проверка API вручную

Используйте Postman, curl или HTTPie для проверки конечных точек:

  • GET /api/users — получить список пользователей
  • POST /api/users — создать пользователя (тело JSON: name, email)
  • PUT /api/users/:id — обновить пользователя
  • DELETE /api/users/:id — удалить пользователя

Пример curl для создания:

curl -X POST http://localhost:3000/api/users -H "Content-Type: application/json" -d '{"name":"Ivan","email":"ivan@example.com"}'

Лучшие практики и рекомендации

  • DTO и class-validator: всегда валидируйте входные данные с помощью DTO и декораторов @IsString, @IsEmail и т.п.
  • Error handling: используйте глобальные фильтры исключений и централизованную обработку ошибок.
  • Автоматические миграции: в production отключите synchronize и применяйте миграции через TypeORM CLI или другой инструмент.
  • Логирование: интегрируйте winston или pino для структурированного логирования.

Важно: synchronize: true удобен для разработки, но потенциально опасен в production.

Альтернативные подходы

  • Prisma вместо TypeORM: Prisma даёт более строгую типизацию и генерацию клиентского API, но требует отдельной модели схемы.
  • Sequelize: опытные команды, использующие Sequelize, могут предпочесть его за гибкость в конфигурировании.
  • Fastify вместо Express: для повышения производительности можно переключить адаптер Nest.js на Fastify.

Краткий выбор: если вам важна строгая типизация и скорость разработки, рассмотрите Prisma; если нужен привычный ORM с декораторами — TypeORM подойдёт.

Когда шаблон CRUD в Nest.js не подходит

  • Сложные транзакционные рабочие процессы: для тяжёлых распределённых транзакций возможно потребуется CQRS/Events.
  • Высокая нагрузка с низкой задержкой: используйте Fastify и кэширование, а также горизонтальное масштабирование.
  • Неподдерживаемые типы данных в ORM: иногда нужно писать сырой SQL или использовать специализированный инструмент.

Модель мышления и эвристики при проектировании CRUD API

  • Разделяй и властвуй: отдельные модули для доменных областей. Модуль — это «граница ответственности».
  • Слой сервиса — это транзакции и бизнес-логика; контроллер — только HTTP-адаптер.
  • Минимизируйте объём данных в эндпоинтах: возвращайте DTO, не сущности напрямую.

Проверочный список для ролей

Для разработчика:

  • Создана сущность и DTO
  • Сервис покрывает CRUD-методы
  • Контроллер валидирует данные
  • Написаны unit-тесты для сервиса

Для DevOps:

  • Переменные окружения защищены
  • Используются миграции, synchronize выключен
  • Настроены резервные копии БД

Для security-инженера:

  • Включена валидация входа
  • Включена авторизация и аудит доступа
  • Ограничены возможности логирования персональных данных

Безопасность и конфиденциальность

  • Всегда валидируйте поля email и любые пользовательские вводы.
  • Храните секреты (DATABASE_URL) вне кода, используйте секретные менеджеры.
  • Маскируйте или не логируйте персональные данные (PII) в продакшн-логах.
  • Рассмотрите защиту API через JWT, OAuth2 или API-ключи в зависимости от сценария.

Заметка по GDPR: если вы храните персональные данные EU-граждан, реализуйте средства для экспортирования и удаления данных по запросу.

Производственные советы и миграции

  • Отключите synchronize и применяйте миграции:
npm run typeorm migration:generate -- -n Init
npm run typeorm migration:run
  • Настройте connection pool: укажите в конфиге параметры максимального числа соединений.
  • Резервное копирование: планируйте регулярные бэкапы базы и тесты восстановления.

Факто-бокс

  • Порт по умолчанию Nest.js dev-сервера: 3000
  • Порт по умолчанию PostgreSQL: 5432
  • Типичные зависимости: @nestjs/typeorm, typeorm, pg, @nestjs/config

Тесты и критерии приёмки

Критерии приёмки:

  • Все CRUD-эндпоинты возвращают корректные коды статуса (200/201/404/204).
  • Входные данные валидируются, некорректные запросы возвращают 400.
  • Создание и обновление корректно сохраняют данные в БД.

Минимальные тесты:

  • Unit: сервис create/find/update/delete с мок-репозиторием.
  • E2E: один сценарий создания -> получение -> обновление -> удаление.

Отладка и распространённые ошибки

  • Ошибка подключения: проверьте DATABASE_URL и доступность порта 5432.
  • Entity не найдена: убедитесь, что сущность импортирована в TypeOrmModule.forFeature и перечислена в forRoot.entities.
  • Некорректные типы параметров в @Param: используйте ParseIntPipe для автоматического преобразования в number.

Пример использования ParseIntPipe в контроллере:

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

@Put(':id')
async update(@Param('id', ParseIntPipe) id: number, @Body() user: User) { ... }

Переезд в продакшн и масштабирование

  • Горизонтальное масштабирование: запустите несколько экземпляров приложения за балансировщиком.
  • Кэширование: используйте Redis для кеширования списков или часто запрашиваемых данных.
  • Мониторинг: подключите Prometheus/Grafana для метрик приложений и БД.

Примеры миграции на Prisma (альтернативный путь)

Коротко: Prisma требует иного подхода — вы описываете схему в prisma/schema.prisma и используете prisma client для запросов. Prisma имеет генерацию типов и удобный миграционный механизм.

Короткая методология внедрения (по шагам)

  1. Инициализация проекта и настройка env.
  2. Подключение TypeORM/Prisma и создание сущностей.
  3. Создание сервисов и контроллеров с DTO и валидацией.
  4. Написание unit и e2e тестов.
  5. Настройка миграций и CI/CD.
  6. Переход в production с мониторингом и бэкапами.

Короткое объявление о релизе (пример для команды, 100–200 слов)

Объявление: Мы внедрили базовую реализацию CRUD REST API для сущности User на основе Nest.js и TypeORM с PostgreSQL. API поддерживает создание, чтение, обновление и удаление пользователей через /api/users и готово для интеграции с фронтендом. Для безопасности включена базовая валидация, а схема хранится в виде TypeORM-сущностей. В production отключите synchronize и используйте миграции. Список задач: подготовить DTO и валидацию, добавить авторизацию и покрыть критические пути тестами. Контакт для вопросов — backend команда.

Резюме

  • Nest.js + TypeORM — быстрый старт для CRUD API.
  • Для production используйте миграции, безопасное хранение секретов и валидацию.
  • Рассмотрите альтернативы (Prisma, Fastify) если нужны иные свойства или производительность.

Ключевые выводы:

  • Разделяйте ответственность — контроллеры для HTTP, сервисы для логики.
  • Включайте DTO и class-validator для надёжной валидации.
  • Тестируйте и готовьте миграции до развёртывания в production.

Важно: перед развертыванием в продакшн проверьте безопасность, резервное копирование и мониторинг.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Объявление переменных в JavaScript — var, let, const
JavaScript

Объявление переменных в JavaScript — var, let, const

Циклы и списки в Python — как перебирать элементы
Python

Циклы и списки в Python — как перебирать элементы

Как копировать файлы в Python быстро
Python

Как копировать файлы в Python быстро

Внедрение зависимостей в PHP с Apex Container
PHP

Внедрение зависимостей в PHP с Apex Container

Исправить UNEXPECTED KERNEL MODE TRAP в Windows 10
Windows 10

Исправить UNEXPECTED KERNEL MODE TRAP в Windows 10

text-shadow в CSS: 11 стильных примеров
CSS

text-shadow в CSS: 11 стильных примеров