基于上一篇内容《为什么现代 Node 后端都选 NestJS + TypeScript?这组合真香了》,这篇文章继续写数据库的连接。
所以今天把MySQL、MongoDB全接上,做个小实例。朋友们项目里用什么数据库可以视情况而定。 这里的功能分别为:
- MySQL:存用户
- MongoDB:存日志
代码短、跟着敲就行。
1. 生成对应架构
执行以下命令生成相关模块代码(Module/Service/Controller)
bash
nest g res user
nest g res log

2. 装包
bash
pnpm i @nestjs/typeorm typeorm mysql2
pnpm i @nestjs/mongoose mongoose
3. 连库
在app.module.ts
一次配好
ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { MongooseModule } from '@nestjs/mongoose';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { TodoModule } from './todo/todo.module';
import { UserModule } from './user/user.module';
import { LogModule } from './log/log.module';
@Module({
imports: [
// MySQL
TypeOrmModule.forRoot({
type: 'mysql',
host: 'localhost',
port: 3306,
username: 'root',
password: 'SV^u8@rB8',
database: 'demo',
autoLoadEntities: true,
synchronize: true, // 仅本地用,生产关掉
}),
// MongoDB
MongooseModule.forRoot('mongodb://localhost:27017/test'),
// 业务模块
TodoModule,
UserModule,
LogModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
代码生成了,接下来我们来处理数据库和增删改查的代码。
Mysql
1. 实体和数据表
数据库表
sql
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`name` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '用户名',
`age` int DEFAULT NULL COMMENT '年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户表';
MySQL 实体
ts
// user.entity.ts
import { Entity, PrimaryGeneratedColumn, Column } from 'typeorm';
@Entity()
export class User {
@PrimaryGeneratedColumn() id: number;
@Column() name: string;
@Column() age: number;
}
Dto
ts
// create-user.dto.ts
export class CreateUserDto {
name: string;
age: number;
}
这里容易出现一个Eslint
问题的爆红,如下图:

出现这种情况执行以下命令:
bash
npx eslint "src/**/*.{ts,js}" --fix --ext .ts,.js
如果还是不行,再执行下这个
bash
npx prettier --write "src/**/*.{ts,js,json}"
2. 业务代码(cv即可)
user.controller.ts
ts
import { Controller, Get, Post, Body, Patch, Param, Delete,} from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
@Get()
findAll() {
return this.userService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.userService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
return this.userService.update(+id, updateUserDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.userService.remove(+id);
}
}
user.module.ts
这里稍微的修改了一下,引入了TypeOrmModule
ts
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { User } from './entities/user.entity';
@Module({
imports: [TypeOrmModule.forFeature([User])],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
user.service.ts
完成了增删改查的业务代码
ts
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { User } from './entities/user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
@Injectable()
export class UserService {
constructor(
@InjectRepository(User)
private readonly repo: Repository<User>,
) {}
async create(createUserDto: CreateUserDto): Promise<User> {
return await this.repo.save(createUserDto);
}
async findAll(): Promise<User[]> {
return await this.repo.find();
}
async findOne(id: number): Promise<User> {
const user = await this.repo.findOne({ where: { id } });
if (!user) {
throw new Error(`User with ID ${id} not found`);
}
return user;
}
async update(id: number, updateUserDto: UpdateUserDto): Promise<User> {
const user = await this.findOne(id);
Object.assign(user, updateUserDto);
return await this.repo.save(user);
}
async remove(id: number): Promise<void> {
const user = await this.findOne(id);
await this.repo.remove(user);
}
}
3. 跑起来,测试下接口
bash
npm run start
新增
POST
http://localhost:3000/user
json
{ "name": "张三", "age": 30 }
修改
POST
http://localhost:3000/user
json
{ "id": 1, "name": "张三", "age": 31 }
删除
DELETE
http://localhost:3000/user/1
查询
GET
http://localhost:3000/user/1
这里实现mysql的增删改查就完成了
MongoDB
大致流程和mysql是一样的,写法稍微有些差别
1. 实体和集合
创建一个集合
sql
db.createCollection("logs");
集合 实体
ts
// log.entity.ts
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document, Types } from 'mongoose';
export type LogDocument = Log & Document;
@Schema()
export class Log {
_id?: Types.ObjectId;
@Prop({ required: true })
message: string;
@Prop({ default: Date.now })
timestamp: Date;
}
export const LogSchema = SchemaFactory.createForClass(Log);
Dto
ts
// create-log.dto.ts
export class CreateLogDto {
message: string;
timestamp?: Date;
}
2. 业务代码(cv即可)
log.controller.ts
ts
import {Controller, Get, Post, Body, Patch, Param, Delete} from '@nestjs/common';
import { LogService } from './log.service';
import { CreateLogDto } from './dto/create-log.dto';
import { UpdateLogDto } from './dto/update-log.dto';
@Controller('log')
export class LogController {
constructor(private readonly logService: LogService) {}
@Post()
create(@Body() createLogDto: CreateLogDto) {
return this.logService.create(createLogDto);
}
@Get()
findAll() {
return this.logService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.logService.findOne(id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateLogDto: UpdateLogDto) {
return this.logService.update(id, updateLogDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.logService.remove(id);
}
}
log.module.ts
引入了MongooseModule
ts
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { LogService } from './log.service';
import { LogController } from './log.controller';
import { Log, LogSchema } from './entities/log.entity';
@Module({
imports: [MongooseModule.forFeature([{ name: Log.name, schema: LogSchema }])],
controllers: [LogController],
providers: [LogService],
})
export class LogModule {}
user.service.ts
增删改查的业务代码
ts
import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
import { CreateLogDto } from './dto/create-log.dto';
import { UpdateLogDto } from './dto/update-log.dto';
import { Log, LogDocument } from './entities/log.entity';
@Injectable()
export class LogService {
constructor(@InjectModel(Log.name) private logModel: Model<LogDocument>) {}
async create(createLogDto: CreateLogDto): Promise<Log> {
const createdLog = new this.logModel(createLogDto);
return createdLog.save();
}
async findAll(): Promise<Log[]> {
return this.logModel.find().exec();
}
async findOne(id: string): Promise<Log> {
const log = await this.logModel.findById(id).exec();
if (!log) {
throw new Error(`Log with ID ${id} not found`);
}
return log;
}
async update(id: string, updateLogDto: UpdateLogDto): Promise<Log> {
const updatedLog = await this.logModel
.findByIdAndUpdate(id, updateLogDto, { new: true })
.exec();
if (!updatedLog) {
throw new Error(`Log with ID ${id} not found`);
}
return updatedLog;
}
async remove(id: string): Promise<void> {
const result = await this.logModel.findByIdAndDelete(id).exec();
if (!result) {
throw new Error(`Log with ID ${id} not found`);
}
}
}
3. 启动,测试接口
bash
npm run start
新增
POST
http://localhost:3000/log
json
{ "message": "张三的消息" }
修改
POST
http://localhost:3000/log
json
{ "id": 1, "message": "李四的消息"}
删除
DELETE
http://localhost:3000/log/1
查询
GET
http://localhost:3000/log/1
MongoDB
的增删改查完成
小结
- MySQL:使用
TypeORM
实体一把梭 - MongoDB:使用
Schema
装饰器直接上
NestJS真的是结构清晰,开发很快,再也不用从头造轮子了。
我是大华,专注分享前后端开发的实战笔记。关注我,少走弯路,一起进步!
📌往期精彩
《Elasticsearch 太重?来看看这个轻量级的替代品 Manticore Search》
《别学23种了!Java项目中最常用的6个设计模式,附案例》