Использование QueryBuilder TypeORM в NestJS
NestJS — это относительно новая веб-инфраструктура JavaScript, которая позволяет создавать серверные приложения корпоративного уровня. Он мощный, применяет лучшие практики и использует новейшие функции JavaScript. Он также имеет поддержку по умолчанию для TypeScript и использует TypeORM , мощную библиотеку управления объектными отношениями, созданную с помощью TypeScript.
TypeORM является одной из самых популярных ORM Node.js. Помимо того, что TypeORM не зависит от базы данных, он имеет уникальный API, который позволяет вам получать доступ к данным в базах данных SQL, таких как MYSQL и PostgreSQL, и базах данных NoSQL, таких как MongoDB, различными способами, используя шаблоны Active Record и Data Mapper.
В этой части мы узнаем, как интегрировать TypeORM с NestJS, добавить драйвер базы данных и выполнять базовые запросы с помощью TypeORM QueryBuilder в NestJS.
Настроить NestJS просто, и есть несколько способов сделать это, в зависимости от ваших потребностей. Однако в этой статье мы установим его с помощью CLI.
npx @nestjs/cli@9.0.0 new nest-app
Эта команда создаст шаблон NestJS «Hello, World!» шаблонное приложение, так что вы можете сразу погрузиться и начать программировать.
Теперь, когда у нас установлен NestJS, давайте интегрируем TypeORM в наше приложение. Обратите особое внимание на этот раздел; вам нужно будет правильно интегрировать TypeORM, прежде чем вы сможете начать писать свои запросы.
Запустите эту команду на своем терминале, чтобы установить драйвер TypeORM и SQLite3 — в этом руководстве мы будем использовать SQLite, чтобы упростить установку и настройку баз данных MySQL или PostgreSQL.
npm install @nestjs/typeorm sqlite3
Создать базовую настройку приложения
Далее давайте создадим контроллеры и службы для скелета нашего приложения запросов, используя интерфейс командной строки NestJS. Выполните следующую команду, чтобы сгенерировать его, и выберите транспортный уровень REST API.
npx @nestjs/cli@9.0.0 g resource posts
Он спросит, хотите ли вы создать точки входа CRUD. Выберите Да . Создание всего за вас займет некоторое время, но это один из полезных аспектов NestJS.
Теперь ваша структура каталогов должна выглядеть так:
. ├── README.md ├── dist ├── nest-cli.json ├── package-lock.json ├── package.json ├── src │ ├── app.controller.spec.ts │ ├── app.controller.ts │ ├── app.module.ts │ ├── app.service.ts │ ├── main.ts │ └── posts │ ├── dto │ │ ├── create-post.dto.ts │ │ └── update-post.dto.ts │ ├── entities │ │ └── post.entity.ts │ ├── posts.controller.spec.ts │ ├── posts.controller.ts │ ├── posts.module.ts │ ├── posts.service.spec.ts │ └── posts.service.ts ├── test ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock
Настройка TypeORM с SQLite
Теперь давайте настроим TypeORM в файле.src/app.module.ts
Изначально это будет выглядеть так:
// src/app.module.ts import { Module } from '@nestjs/common'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { PostsModule } from './posts/posts.module'; @Module({ imports: [PostsModule], controllers: [AppController], providers: [AppService], }) export class AppModule {}
Импортируйте параметры подключения SQLite, модуль TypeORM и сущность сообщения, как показано в приведенном ниже коде:
// src/app.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { SqliteConnectionOptions } from 'typeorm/driver/sqlite/SqliteConnectionOptions'; import { AppController } from './app.controller'; import { AppService } from './app.service'; import { Post } from './posts/entities/post.entity'; import { PostsModule } from './posts/posts.module'; const config: SqliteConnectionOptions = { type: "sqlite", database: "../db", entities: [Post], synchronize: true } @Module({ imports: [PostsModule, TypeOrmModule.forRoot(config)], controllers: [AppController], providers: [AppService], }) export class AppModule {}
Теперь давайте поработаем с обновлением, которое мы только что добавили. Во-первых, глядя на форму объекта конфигурации, мы добавили базу данных и сущности и синхронизировали базу данных. Однако важно помнить, что синхронизацию базы данных не следует выполнять в производственной среде, так как это может привести к потере данных.
// the config const config: SqliteConnectionOptions = { type: "sqlite", database: "../db", entities: [Post], synchronize: true // set to false on production }
Поскольку мы используем базу данных SQLite, мы можем быстро добавить путь к базе данных с расширением . Если он не существует, он будет автоматически создан для вас. Для MySQL или PostgreSQL формы отличаются, поэтому ознакомьтесь с документацией , чтобы узнать больше."../db",
Создание объектов базы данных
Сущности — это модели базы данных, и в нашем случае наша Post
сущность имеет идентификатор и заголовок, как показано в коде ниже:
// src/post/entities/post.entity.ts import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; @Entity() export class Post { @PrimaryGeneratedColumn() id: number; @Column() title: string; }
Наконец, в общей настройке приложения TypeORM давайте подключим конфигурацию TypeORM с помощью NestJS. Обратите внимание на этот метод, потому что вы можете увидеть нечто подобное при настройке конфигурации на уровне функций..forRoot
imports: [PostsModule, TypeOrmModule.forRoot(config)],
Здесь у нас есть TypeORM, подключенный к NestJS. Далее давайте интегрируем нашу Post
функцию с ORM.
Перейдите к файлу и обновите его со следующей конфигурацией:src/posts/posts.module.ts
// src/posts/posts.module.ts import { Module } from '@nestjs/common'; import { PostsService } from './posts.service'; import { PostsController } from './posts.controller'; @Module({ controllers: [PostsController], providers: [PostsService] }) export class PostsModule {}
Затем импортируйте объект TypeORM Post
и обновите код, установив значение импорта модуля с помощью . Обратите внимание, что мы используем и передаем массив сущностей, а не конфигурацию модуля на уровне приложения.[TypeOrmModule.forFeature([Post])]forFeature
import { Module } from '@nestjs/common'; import { PostsService } from './posts.service'; import { PostsController } from './posts.controller'; import { TypeOrmModule } from '@nestjs/typeorm'; import { Post } from './entities/post.entity'; @Module({ imports: [TypeOrmModule.forFeature([Post])], controllers: [PostsController], providers: [PostsService] }) export class PostsModule {}
Выполнение базовых запросов с помощью TypeORM QueryBuilder в NestJS
Существует несколько способов доступа к базе данных с помощью TypeORM и NestJS, включая использование API-интерфейса репозитория, API-интерфейса Entity Manager и API-интерфейса DataSource.
Ниже приведен краткий пример того, как получить элемент по идентификатору с помощью упомянутых выше API.
//src/posts/posts.service.ts @Injectable() export class PostsService { constructor( @InjectRepository(Post) private postRepository: Repository<Post>, @InjectEntityManager() private postManager: EntityManager, @InjectDataSource() private dataSource: DataSource ) { } async findOne(id: number) { const postWithRepository = await this.postRepository.findOneBy({ id }); const postWithRepositoryQueryBuilder = await this.postRepository .createQueryBuilder("post") .where("post.id= :postId", { postId: id }) .getOne() const postWithEntityManager = await this.postManager .createQueryBuilder(Post, "post") .where("post.id= :postId", { postId: id }) .getOne() const postWithDataSource = await this.dataSource .createQueryBuilder() .select("post") .from(Post, "post") .where("post.id= :postId", { postId: id }) .getOne() return { postWithRepository, postWithRepositoryQueryBuilder, postWithEntityManager, postWithDataSource }; } }
Как видите, мы инициализировали слои доступа к данным в конструкторе, а затем использовали их внутри методов.
//src/posts/posts.service.ts ... constructor( @InjectRepository(Post) private postRepository: Repository<Post>, ) { } ...
Помимо этого, остальная часть кода — это обычные запросы TypeORM; вы можете глубже изучить TypeORM, ознакомившись с их документацией .
Все запросы в приведенном выше коде вернут один и тот же результат. Итак, есть несколько способов достижения одной цели, но какой из них самый эффективный?
При работе с небольшими наборами данных оба метода работают одинаково. Однако API QueryBuilder более эффективен, чем запросы репозитория, при работе с большими наборами данных с несколькими отношениями. Возможно, это связано с тем, что API QueryBuilder относительно ближе к необработанным SQL-запросам, чем API репозитория.
Использование JOIN
запросов в TypeORM с NestJS
Если вы написали SQL-запрос, который включает доступ к данным из нескольких таблиц с помощью SQL, скорее всего, вы писали запрос JOIN
раньше. JOIN
запросы позволяют запрашивать данные из нескольких таблиц одновременно. Давайте посмотрим на LEFT JOIN
операцию в TypeORM. Этот тип запроса вернет все строки из левой таблицы и соответствующие строки из правой таблицы.
Есть несколько способов выполнить LEFT JOIN
операцию в TypeORM. Рассмотрим некоторые из них:
- Использование
find
опций - Использование QueryBuilder
Несмотря на то, что мы сосредоточены на использовании QueryBuilder, мы покажем вам пример, в котором используются оба параметра, чтобы вы могли увидеть различия между ними.
Давайте посмотрим на эту LEFT JOIN
операцию SQL ниже. Мы преобразуем его, чтобы использовать find
опцию и метод QueryBuilder TypeORM.
SELECT * FROM "user" LEFT JOIN "courses" "course" ON "course"."id" = "user"."courseId" WHERE "course"."name" = 'JavaScript Fundamentals' AND "course"."length" = '8 hours'
Использование find
в TypeORM
userRepository.find({ relations: { course: true }, where: { course: { name: "JavaScript Fundamentals", length: "8 hours" }, }, })
Приведенный выше код представляет собой LEFT JOIN
запрос с использованием параметра TypeORM find
. К счастью, это просто, потому что TypeORM определяет JOIN
для вас лучшее и дает вам соответствующий результат. Теперь давайте реализуем вышеуказанный запрос с помощью TypeORM QueryBuilder.
Использование QueryBuilder для JOIN
const user = this.userManager .createQueryBuilder(User, "user") .leftJoin("course"."id", "course") .where("course.name = :name", { name: "JavaScript Fundamentals" }) .andWhere("course.length = :length", { length: "8 hours" })
При использовании QueryBuilder у вас также есть опции для разных типов JOINS
, в отличие от find
опции, которая делает все «под капотом».
Вот некоторые дополнительные JOINS
, доступные в TypeORM.
Заключение
Если вы хотите быстро создать надежную серверную часть без ущерба для качества, рассмотрите возможность использования с TypeORM. В этой статье мы узнали, как интегрировать и использовать TypeORM QueryBuilder с NestJS, но мы только коснулись того, что вы можете делать с TypeORM. Ознакомьтесь с документацией по TypeORM, чтобы узнать больше .