Nestjs🐺⚡ | Nodejs 框架(第三部分)| 数据库集成,TypeORM
在这一部分,我将讨论
- Nestjs 数据库集成
- 基本 Typeorm
数据库集成
后端服务/API 的主要部分是其数据库管理系统
重要提示:本文是关于 Nestjs 的,而不是关于数据库(SQL/NoSQL),所以我假设阅读本文的人对 DBMS 有基本的了解
由于 Nestjs 只是对典型 Nodejs 服务器 API/包的抽象,因此它支持各种流行的数据库及其大多数 ORM。它支持以下数据库驱动程序和 ORM:
- Typeorm(SQL/NoSQL)
- MikroORM(SQL/NoSQL)
- 克内克斯
- 棱镜
- 猫鼬(NoSQL)
我在这里使用 TypeORM,因为在我看来,它与 Nestjs 的 API 方案最为匹配,因为它采用了装饰器模式。此外,我还使用 PostgreSQL 作为数据库。你也可以根据需要使用其他数据库和 ORM。它们的配置模式基本相同。此外,总有官方/第三方软件包和文档供你选择。谷歌一下就行了。
首先安装:
# for npm users*
$ npm i @nestjs/typeorm typeorm psql
# for yarn user
$ yarn add @nestjs/typeorm typeorm psql
注意!:请先在操作系统中安装 PostgreSQL 驱动程序或您选择的数据库驱动程序。PostgreSQL 的安装说明可在此处找到。
现在在项目根目录中创建以下文件:
.env
(用于存储数据库凭证和机密)config.ts
(用于导入环境变量)ormconfig.ts
(数据库连接配置)
#### .env #####
POSTGRES_PASSWORD=simplepassword
POSTGRES_DB=hello
NODE_ENV=development
DATABASE_USERNAME=postgres # you can put your username of your OS
DATABASE_HOST=localhost # use `postgres` if using PostgreSQL Docker Container
DATABASE_PORT=5432
PORT=4000
现在导入此环境变量并重新导出项目
///// config.ts //////
export const NODE_ENV = process.env.NODE_ENV;
// all the env vars
export const DATABASE_HOST = process.env.DATABASE_HOST;
export const DATABASE_PORT = process.env.DATABASE_PORT
? parseInt(process.env.DATABASE_PORT)
: undefined;
export const DATABASE_NAME = process.env.POSTGRES_DB;
export const DATABASE_PASSWORD = process.env.POSTGRES_PASSWORD;
export const DATABASE_USERNAME = process.env.DATABASE_USERNAME;
export const PORT = process.env.PORT ?? 4000;
创建数据库连接:
///// ormconfig.ts /////
import {
DATABASE_HOST,
DATABASE_NAME,
DATABASE_PASSWORD,
DATABASE_PORT,
DATABASE_USERNAME,
NODE_ENV,
} from "./config";
import { PostgresConnectionOptions } from "typeorm/driver/postgres/PostgresConnectionOptions";
// db configuration for the orm
const ormconfig: PostgresConnectionOptions = {
type: "postgres", // name of db you'll be using
username: DATABASE_USERNAME,
database: DATABASE_NAME,
host: DATABASE_HOST,
port: DATABASE_PORT,
password: DATABASE_PASSWORD,
uuidExtension: "uuid-ossp", // for using `uuid` as the type for Primary-Column `id` column
synchronize: NODE_ENV !== "production",
};
export = ormconfig;
现在生成一个名为的模块database
,用于保存所有与数据库相关的配置/文件。以下命令将生成它:
$ npx nest g module database
database.module.ts
在配置里面使用 TypeORM 注册数据库连接:
///// database.module.ts //////
import { Module } from '@nestjs/common';
import ormconfig from "../../ormconfig";
import { TypeOrmModule } from "@nestjs/typeorm";
@Module({
imports: [
// registers Database config
TypeOrmModule.forRoot({
...ormconfig, //db config
entities: [], // put the constructor of all classes that are an Entity
}),
],
})
export class DatabaseModule {}
希望在重新启动应用程序后,您的 API/服务将与数据库连接
提示!:你可以在函数中传递一个 ORM 配置数组,
TypeOrmModule.forRoot()
用于多个数据库连接。这样可以同时使用任何类型的数据库。
类型ORM
本教程并非关于 TypeORM 的,但我会对其进行一些解释,以便入门。更多信息请参阅他们的文档。
TypeORM 的 SQL/NoSQL API 各有不同。这里我仅展示 SQL 部分。如果您使用的是 NoSQL 数据库(例如 MongoDB),并且使用了 TypeORM,那么您可以点击此处进行学习。
如果你读过第二部分,你可能知道,我之前用类属性作为内存临时数据库。现在我们将通过反射器实现这一部分,以便通过 TypeOrm 使用新的 PostgreSQL 数据库。
首先,创建src/database/entities/hello-record.entity.ts
,然后创建 TypeORM 模式:
///// hello-record.entity.ts /////
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity('hello-record')
export class HelloRecord {
@PrimaryGeneratedColumn('uuid')
id!: string;
@Column('varchar', { length: 16 })
from!: string;
@Column('text')
msg!: string;
}
你可以使用装饰器将一个类声明为 TypeORM 实体@Entity()
。你可以为该实体指定一个名称(你应该始终这样做),或者让 TypeORM 根据该类的显示名称创建一个名称。
要创建主列,@PrimaryGeneratedColumn
请使用 。您可以使用rowId
、incremented
或uuid
作为主列类型。请记住启用 PostgreSQL 数据库或任何 SQL 数据库的 UUID 扩展,以便使用 uuid
要创建列,@Column
需要使用装饰器。您可以指定列的类型或该列的任何内容。我varchar
为列“ from
”使用了 16 个字符的长度,因为它将用于存储发布问候消息的用户的 IP 地址。text
此外,我为“ ”列使用了类型,msg
因为我们不想像某些社交媒体那样将每个人的长度限制为 240 个字符。这太不人道了🤐
现在,为了让 TypeORM 知道HelloRecord
它的存在,我们必须将它放入函数entities
数组Typeorm.forRoot()
中database.module.ts
。你必须将应用程序中使用的所有实体都放入该数组中。顺便说一句,如果你使用多个数据库连接,请将为特定数据库专门创建的实体放入特定数据库的配置对象entities
数组中。同一个实体不能用于多个数据库。
///// database.module.ts //////
// .... (other imported stuffs)
import { HelloRecord } from './entities/hello-record.entity';
@Module({
imports: [
TypeOrmModule.forRoot({
...ormconfig,
// put all the entities related to the database in here
entities: [
HelloRecord,
],
}),
],
})
export class DatabaseModule {}
重要提示!:切勿在实体/模式声明中使用继承。如果某些实体共享相同类型的属性/列,请勿为它们创建一个通用类,然后从中继承。这可能会导致模式同步问题,因为 TypeORM 无法获知该父类属性的类型。模式继承并非 DRY 原则,而是对 DRY(不要重复自己)的过度使用。所以永远不要这样做。
现在我们已经创建了实体,让我们在我们的中使用它HelloService
。但我们必须将其导入,HelloModule
以便让 Nest 知道它属于HelloModule
////// hello.module.ts //////
// .... (other imported stuff)
import { TypeOrmModule } from '@nestjs/typeorm';
@Module({
imports: [
forwardRef(() => HiModule),
// labelling the entity as `HelloModule`'s Repository
TypeOrmModule.forFeature([HelloRecord]),
],
providers: [HelloService, ByeService],
controllers: [HelloController],
exports: [HelloService],
})
export class HelloModule {}
TypeOrmModule.forFeature
将允许访问 的HelloRecord
所有提供程序/控制器中的实体HelloModule
。顺便说一句,您不能在不同模块中多次标记同一个实体。如果您想在其他模块中访问该实体,只需导入使用该实体的提供程序即可。
现在让我们重构HelloService
以使用新的实体来保存、修改和读取hello 消息:
////// hello.service.ts ///////
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { HiService } from 'src/hi/hi.service';
import { ByeService } from './bye.service';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { HelloRecord } from '../database/entities/hello-record.entity';
@Injectable()
export class HelloService {
constructor(
@Inject(forwardRef(() => HiService))
private hiService: HiService,
@Inject(forwardRef(() => ByeService))
private byeService: ByeService,
@InjectRepository(HelloRecord)
private helloRecordRepo: Repository<HelloRecord>,
) {}
async findById(id: string) {
return await this.helloRecordRepo.findOneOrFail({ id });
}
async create(msg: string, ip: string) {
const newMsg = this.helloRecordRepo.create({ msg, from: ip });
return await newMsg.save();
}
async deleteById(id: string) {
return await this.helloRecordRepo.delete({ id });
}
getHello(arg: string) {
return `hello for ${arg}`;
}
// a method that uses `hiService`
hiServiceUsingMethod() {
return this.hiService.getHi('hello');
}
byeServiceUsingMethod() {
return this.byeService.getBye('hello');
}
TypeORM 提供了创建、删除和修改数据所需的所有方法。以下是一些最常用的方法:
EntityName.findOne
(按条件查找并返回第一个匹配的记录作为承诺)EntityName.findOneOrFail
(就像findOne
但如果没有找到记录则会抛出错误。总是尝试使用它而不是findOne
因为它支持错误处理)EntityName.find
(查找所有符合条件的记录并以承诺形式返回)EntityName.save
(保存传递给它的任何与该实体的模式匹配的对象。也可用于修改/更新记录)EntityName.create
(创建一个新的软记录,并将其作为参数传递给EntityName.save
)EntityName.delete
(删除所有符合传递条件的记录)EntityName.createQueryBuilder
(替代查询 API,使用字符串来操作 SQL 事务,而不是使用面向对象的方法。它更像是一种函数式方法。它遵循流行的构建器模式并支持方法链。它更接近原生 SQL)
最终工作应用程序:
到这里,我结束Nestjs🐺⚡ | Nodejs 的框架系列
Nestjs 是一个非常棒的后端框架,提供了开发企业级、可靠的服务器应用程序/API 所需的所有必要工具。本系列文章只是对 Nestjs 的更高层次的概述,只涉及了很少的概念和功能。Nestjs 提供的远不止这些。Nest 的官方文档提供了关于 Nestjs 更深入、更清晰的概述和教程。他们还提供企业支持。
鏂囩珷鏉ユ簮锛�https://dev.to/krtirtho/nestjs-the-framework-of-nodejs-part-3-database-integration-typeorm-4gabNestjs 不仅仅是一个工具/框架/库,它也是你的指南