Вместо непосредственной работы с базами данных SQL я обнаружил, что использование TypeORM значительно упрощает процесс работы с базами данных и сокращает время разработки. Поэтому я хотел бы рекомендовать использовать TypeORM всем, кто хочет более эффективно работать с базами данных.

TypeORM — это библиотека объектно-реляционного сопоставления (ORM) для TypeScript и JavaScript, которая позволяет разработчикам работать с реляционными базами данных с использованием методов объектно-ориентированного программирования. Он предоставляет богатый набор функций для определения схемы базы данных, запроса данных и управления соединениями с базой данных. TypeORM поддерживает несколько систем баз данных, включая MySQL, PostgreSQL, Oracle и SQLite.

NestJS обеспечивает встроенную поддержку TypeORM, позволяя разработчикам использовать его для взаимодействия с базами данных в своих приложениях. NestJS использует внедрение зависимостей для предоставления экземпляров Repository классов другим компонентам в приложении. Разработчики могут определить класс Provider для класса Repository, а затем внедрить его в другие компоненты, которым необходимо взаимодействовать с базой данных. NestJS также предоставляет встроенную поддержку для определения контроллеров, служб и модулей, использующих репозитории TypeORM, что упрощает создание сложных приложений, управляемых базой данных.

Вот пример простого приложения NestJS CRUD, использующего PostgreSQL в качестве базы данных и библиотеку TypeORM в качестве ORM.

Во-первых, давайте создадим новую базу данных PostgreSQL и создадим таблицу с именем todos:

CREATE DATABASE nestjs_postgres_crud;
CREATE TABLE todos (
  id SERIAL PRIMARY KEY,
  title TEXT NOT NULL,
  description TEXT,
  completed BOOLEAN NOT NULL DEFAULT FALSE
);

В PostgreSQL serial — это псевдотип данных, используемый для определения столбца с автоинкрементом. Когда столбец определен как serial, PostgreSQL автоматически создает объект последовательности и устанавливает значение столбца по умолчанию на следующее значение последовательности. Это означает, что каждый раз, когда в таблицу вставляется новая строка, значение столбца serial будет автоматически увеличиваться на 1.

Далее давайте создадим сущность Todo с помощью TypeORM:

// src/todo/todo.entity.ts

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

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

  @Column()
  title: string;

  @Column({ nullable: true })
  description: string;

  @Column({ default: false })
  completed: boolean;
}

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

  • @Entity(): Этот декоратор используется для определения нового объекта в TypeORM. Его следует применять к классу, представляющему объект.
  • @Column(): этот декоратор используется для определения нового столбца в объекте. Его следует применять к свойству, представляющему столбец.
  • @PrimaryGeneratedColumn(): этот декоратор используется для определения нового столбца первичного ключа в объекте. Его следует применять к свойству, представляющему первичный ключ.

Теперь давайте создадим TodoService, который будет обрабатывать операции CRUD:

// src/todo/todo.service.ts

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Todo } from './todo.entity';

@Injectable()
export class TodoService {
  constructor(
    @InjectRepository(Todo)
    private readonly todoRepository: Repository<Todo>,
  ) {}

  async findAll(): Promise<Todo[]> {
    return this.todoRepository.find();
  }

  async findById(id: number): Promise<Todo> {
    return this.todoRepository.findOne(id);
  }

  async create(todo: Todo): Promise<Todo> {
    return this.todoRepository.save(todo);
  }

  async update(id: number, todo: Todo): Promise<Todo> {
    await this.todoRepository.update(id, todo);
    return this.todoRepository.findOne(id);
  }

  async delete(id: number): Promise<void> {
    await this.todoRepository.delete(id);
  }
}

@InjectRepository — это декоратор, предоставляемый пакетом @nestjs/typeorm в NestJS, который можно использовать для внедрения экземпляра репозитория TypeORM в службу или контроллер.

Когда вы добавляете декоратор @InjectRepository к свойству класса или параметру конструктора, NestJS автоматически внедряет экземпляр соответствующего репозитория во время выполнения. Например, в классе TodoService следующая строка:

@InjectRepository(Todo)
private readonly todoRepository: Repository<Todo>,

внедряет экземпляр класса Repository<Todo>, который представляет репозиторий TypeORM для сущности Todo.

Декоратор @InjectRepository работает, используя систему внедрения зависимостей, предоставляемую NestJS. Под капотом он создает новый экземпляр репозитория для каждого запроса и гарантирует, что один и тот же экземпляр используется на протяжении всего жизненного цикла запроса. Это помогает избежать проблем с репозиториями с отслеживанием состояния и гарантирует, что каждый запрос обрабатывается независимо.

Класс Repository в TypeORM предоставляет несколько методов CRUD (создание, чтение, обновление, удаление) для работы с сущностями в базе данных. Вот некоторые из наиболее часто используемых методов:

  • find(options?: FindManyOptions<Entity>): Promise<Entity[]>: возвращает массив всех объектов, соответствующих указанным критериям FindManyOptions, или пустой массив, если объекты не найдены.
  • findOne(id: string | number | Date | ObjectID, options?: FindOneOptions<Entity>): Promise<Entity | undefined>: возвращает один объект, соответствующий указанному идентификатору или критериям, или undefined, если объект не найден.
  • save(entity: Entity, options?: SaveOptions): Promise<Entity>: Сохраняет указанный объект в базе данных и возвращает сохраненный объект.
  • update(criteria: string | number | Date | ObjectID | FindConditions<Entity>, partialEntity: QueryDeepPartialEntity<Entity>): Promise<UpdateResult>: обновляет один или несколько объектов, соответствующих указанным критериям, и возвращает объект, содержащий информацию об операции обновления.
  • delete(criteria: string | number | Date | ObjectID | FindConditions<Entity>): Promise<DeleteResult>: удаляет один или несколько объектов, соответствующих указанным критериям, и возвращает объект, содержащий информацию об операции удаления.
  • count(options?: FindManyOptions<Entity>): Promise<number>: возвращает общее количество объектов, соответствующих указанным FindManyOptions критериям.

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

Наконец, давайте создадим TodoController, который будет обрабатывать HTTP-запросы:

// src/todo/todo.controller.ts

import { Controller, Get, Post, Put, Delete, Param, Body } from '@nestjs/common';
import { Todo } from './todo.entity';
import { TodoService } from './todo.service';

@Controller('todos')
export class TodoController {
  constructor(private readonly todoService: TodoService) {}

  @Get()
  async findAll(): Promise<Todo[]> {
    return this.todoService.findAll();
  }

  @Get(':id')
  async findById(@Param('id') id: string): Promise<Todo> {
    return this.todoService.findById(parseInt(id, 10));
  }

  @Post()
  async create(@Body() todo: Todo): Promise<Todo> {
    return this.todoService.create(todo);
  }

  @Put(':id')
  async update(@Param('id') id: string, @Body() todo: Todo): Promise<Todo> {
    return this.todoService.update(parseInt(id, 10), todo);
  }

  @Delete(':id')
  async delete(@Param('id') id: string): Promise<void> {
    return this.todoService.delete(parseInt(id, 10));
  }
}

Вот и все! Теперь вы можете запустить сервер NestJS с помощью npm run start:dev и протестировать API с помощью таких инструментов, как Postman или curl.

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

Если вы найдете эту информацию полезной, поддержите меня и следите за обновлениями.🙂