NestJS 实战:连接 MySQL、数据操作与接口文档配置

NestJS 实战:连接 MySQL、数据操作与接口文档配置

连接MySQL

路由已生效,作为后端项目,必须引入数据库功能,否则与开发静态页面无异。MySQL作为实际项目中的主流选择,本文将详细介绍其安装、连接和使用方法,并分享可能遇到的问题。已有经验的开发者可跳过此部分。

数据库安装

若本地未安装MySQL且无云数据库可用,请先通过官网下载MySQL Community Server对应版本。推荐使用Navicat for MySQL或SQLyog等可视化工具管理数据库,选择习惯使用的即可。

TypeORM连接数据库

前置知识

ORM(对象关系映射)技术将关系型数据库的表结构映射为JavaScript对象。相比直接使用Node.js操作MySQL底层接口,ORM框架(如TypeORM、Sequelize、Prisma)能简化操作。例如:

原始插入语句:

connection.query(`INSERT INTO posts (title, content) VALUES ('${title}', '${content}')`, (err, data) => {
    if (err) console.error(err);
    else console.log(data);
});

使用TypeORM后简化为:

return await this.userRepository.save(createUserDto);
安装依赖
npm install @nestjs/typeorm typeorm mysql2 -S

官方提供两种连接方式:

方法1:环境变量配置

  1. 创建.env.env.prod文件存储开发/生产环境变量:
DB_HOST=localhost
DB_PORT=3306
DB_USER=root
DB_PASSWD=root
DB_DATABASE=blog

注意:.env.prod应加入.gitignore

  1. 创建config/env.ts读取配置:
import * as fs from 'fs';
import * as path from 'path';

const isProd = process.env.NODE_ENV === 'production';

function parseEnv() {
  const localEnv = path.resolve('.env');
  const prodEnv = path.resolve('.env.prod');

  if (!fs.existsSync(localEnv) && !fs.existsSync(prodEnv)) {
    throw new Error('缺少环境配置文件');
  }

  const filePath = isProd && fs.existsSync(prodEnv) ? prodEnv : localEnv;
  return { path: filePath };
}

export default parseEnv();
  1. app.module.ts配置连接:
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'rootroot',
      database: 'nestjs',
      entities: [__dirname + '/**/*.entity{.ts,.js}'],
      synchronize: true,
    }),
  ],
})
export class AppModule {}

推荐使用@nestjs/config管理环境变量。

方法2:ormconfig.json配置

  1. 创建ormconfig.json
{
  "type": "mysql",
  "host": "localhost",
  "port": 3306,
  "username": "root",
  "password": "root",
  "database": "blog",
  "entities": ["dist/**/*.entity{.ts,.js}"],
  "synchronize": true
}
  1. app.module.ts中直接调用:
import { TypeOrmModule } from '@nestjs/typeorm';

@Module({
  imports: [TypeOrmModule.forRoot()],
})
export class AppModule {}

更多连接方式请参考TypeORM官方文档。至此,数据库连接已成功建立。

数据操作

创建用户表

通过TypeORM的实体映射机制创建数据库表。在user/entities/user.entity.ts中定义用户实体:

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

@Entity("user")
export class UserEntity {
  @PrimaryGeneratedColumn()
  id: number; // 自动生成的主键

  @Column({ length: 20 })
  username: string;

  @Column({ length: 20 })
  password: string;

  @Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
  create_time: Date;

  @Column({ type: "timestamp", default: () => "CURRENT_TIMESTAMP" })
  update_time: Date;
}

保存后系统会自动创建user表。

CRUD操作

模块配置

user.module.ts中导入数据库相关模块:

import { Module } from "@nestjs/common";
import { UserService } from "./user.service";
import { UserController } from "./user.controller";
import { UserEntity } from "./entities/user.entity";
import { TypeOrmModule } from "@nestjs/typeorm";

@Module({
  imports: [TypeOrmModule.forFeature([UserEntity])],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}
创建用户

定义接收参数DTO:

// create-user.dto.ts
export class CreateUserDto {
  username: string;
  password: string;
}

服务层实现:

// user.service.ts
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';
import { UserEntity } from './entities/user.entity';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(UserEntity)
    private userRepository: Repository<UserEntity>,
  ) {}

  async create(createUserDto: CreateUserDto) {
    return await this.userRepository.save(createUserDto);
  }
}
查询用户

基础查询:

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

带条件查询:

async findAll() {
  return await this.userRepository.find({ where: { id: 1 } });
}
更新用户

控制器接收参数:

@Patch(':id')
update(@Param('id') id: string, @Body() updateUserDto: UpdateUserDto) {
  return this.userService.update(+id, updateUserDto);
}

服务层实现(使用TypeORM查询构造器):

async update(id: number, updateUserDto: UpdateUserDto) {
  const qb = await this.userRepository.createQueryBuilder();
  return await qb.update().set(updateUserDto).where({ id }).execute();
}
删除用户
async remove(id: number) {
  const qb = await this.userRepository.createQueryBuilder();
  return await qb.delete().where({ id }).execute();
}

统一接口返回格式

异常过滤器

创建HTTP异常过滤器:

// http-exception.filter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';

@Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: HttpException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    const status = exception.getStatus();

    response.status(status).json({
      code: status,
      timestamp: new Date().toISOString(),
      path: request.url,
    });
  }
}

在main.ts中注册:

import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { HttpExceptionFilter } from "./common/filter/http-exception/http-exception.filter";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalFilters(new HttpExceptionFilter());
  await app.listen(3000);
}
bootstrap();

响应拦截器

创建响应拦截器:

// transform.interceptor.ts
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';

export interface Response<T> {
  data: T;
}

@Injectable()
export class TransformInterceptor<T> implements NestInterceptor<T, Response<T>> {
  intercept(context: ExecutionContext, next: CallHandler): Observable<Response<T>> {
    return next
      .handle()
      .pipe(map((data) => ({ code: 200, data, describe: '请求成功' })));
  }
}

在main.ts中注册:

app.useGlobalInterceptors(new TransformInterceptor());

配置接口文档 Swagger

下面介绍如何使用Swagger高效地编写接口文档。选择Swagger的原因有二:一是Nest.js提供了专用模块支持,二是它能精确展示每个字段的含义(前提是注解写到位)。

安装配置

首先安装依赖:

npm install @nestjs/swagger swagger-ui-express -S

本文使用的是5.1.4版本,注意与4.x.x版本存在API差异。

在main.ts中配置Swagger文档信息:

import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);
  
  // Swagger配置
  const config = new DocumentBuilder()
    .setTitle('管理后台')   
    .setDescription('管理后台接口文档')
    .setVersion('1.0')
    .addBearerAuth()
    .build();
  const document = SwaggerModule.createDocument(app, config);
  SwaggerModule.setup('docs', app, document);

  await app.listen(3000);
}
bootstrap();

配置完成后,访问 http://localhost:3000/docs 即可查看生成的文档。

接口标签分类

可以根据Controller进行分类,只需添加@ApiTags注解:

import { ApiTags } from '@nestjs/swagger';
import { Body, Controller, Delete, Get, Param, Post, Put, Query } from '@nestjs/common';

@ApiTags("文章")
@Controller('post')
export class PostsController {...}

对所有Controller添加分类标签后,刷新文档即可看到分类效果。

接口说明

通过@ApiOperation装饰器为每个接口添加说明,使接口功能一目了然:

import { ApiTags, ApiOperation } from '@nestjs/swagger';

export class PostsController {
  @ApiOperation({ summary: '创建文章' })
  @Post()
  async create(@Body() post) {....}
  
  @ApiOperation({ summary: '获取文章列表' })
  @Get()
  async findAll(@Query() query): Promise<PostsRo> {...}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值