nestjs以及使用sequlize操作数据库

参考文章:
[1] nodejs/Sequelize/MySQL——基础、联表及优化
[2]在EggJS中使用Sequelize做联表查询

1 在控制器中设置路由

访问的地址是:http://localhost:3000/cats/profile

@Controller("cats")
export class CatsController {
  @Get("profile")
  findAll(): string {
    return "this is action";
  }
}

2 获取 get 参数

http://localhost:3000/cats/profile?debug=true&name=xiaohua

@Controller("cats")
export class CatsController {
  @Get("profile")
  findAll(@Query() request: Request): string {
    console.log(request); //{ debug: 'true', name: 'xiaohua' }
    return "this is action";
  }
}

3 获取 post 参数

post 请求
http://localhost:3000/cats/doit

import { Controller, Get, Query, Body, Post } from "@nestjs/common";
import { Request } from "express";

@Controller("cats")
export class CatsController {
  @Post("doit")
  findDoit(@Body() newData: Request): string {
    console.log(newData);
    return "this is post";
  }
}

获取到入参{ componentName: 'backtop', address: '北京' }

4 获取 ip

import { Controller, Get, Query, Ip } from "@nestjs/common";
import { Request } from "express";

@Controller("cats")
export class CatsController {
  @Get("profile")
  findAll(@Query() request: Request, @Ip() ip: Request): string {
    return JSON.stringify(request) + "--" + ip;
  }
}

5 设置响应头

import { Controller, Get, Query, Ip, Header } from "@nestjs/common";
import { Request } from "express";

@Controller("cats")
export class CatsController {
  @Get("profile")
  @Header("Cache-Control", "no-store") //设置响应头
  findAll(@Query() request: Request, @Ip() ip: Request): string {
    return JSON.stringify(request) + "--" + ip;
  }
}

6 设置动态路由

访问链接:http://localhost:3000/cats/123

import { Controller, Get, Param } from "@nestjs/common";

@Controller("cats")
export class CatsController {
  @Get(":id")
  findAll(@Param() params): string {
    return params.id; //123
  }
}

7 service 完整示例

service 端

service/cats/cats.service.ts

import {Injectable} from '@nestjs/common';
import {Cat} from '../../interface/cat.interface';

@Injectable()
export class CatsService {
	private readonly cats: Cat[] = [];
	create(cat: Cat) {
		this.cats.push(cat);
	}
	findAll(): Cat[] {
		return this.cats;
	}
}

interface/cat.interface.ts

export interface Cat {
  name: string;
  age: number;
  breed: string;
}

controller/cats/cats.controller.ts

import {Body, Controller, Get, Param, Post} from '@nestjs/common';
import {Cat} from '../../interface/cat.interface';
import {CatsService} from '../../service/cats/cats.service';
@Controller('cats')
export class CatsController {
	constructor(private catsService: CatsService) {}
	@Post('saveData')
	async create(@Body() catsData: Cat) {
		await this.catsService.create(catsData);
		return 'success';
	}

	@Get('info')
	async getAll() {
		return this.catsService.findAll();
	}
}

也就是 controller 设置的路由,由异步方式,调用的 service,由 service 处理服务器端

8 全局中间件

如果我们想一次性将中间件绑定到每个注册路由,我们可以使用由 INestApplication 实例提供的 use()方法:

import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";
import { logger } from "./middleware/loggerfun.middleware";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.use(logger);
  await app.listen(3000);
}
bootstrap();

注意使用的全局中间件是函数式中间件

export function logger(req, res, next) {
  console.log("req", req.originalUrl);
  next();
}

10 管道 pipe 的使用方法

pipe 用来过滤数据,还可以处理数据

Controller 文件

import {Body, Controller, Get, HttpException, HttpStatus, Post} from '@nestjs/common';
import {AppService} from './app.service';
import {SaveData} from './interface/saveData.interface';
import {ValidationPipe} from './pipe/validate.pipe';

@Controller()
export class AppController {
	constructor(private readonly appService: AppService) {}
	@Post('list')
	async saveList(@Body(new ValidationPipe()) saveData: SaveData) { //在入参 saveData 中设置了管道来进行拦截
		return this.appService.saveList(saveData);
	}
}

其中管道 validate.pipe 定义

import {
  PipeTransform,
  Injectable,
  ArgumentMetadata,
  BadRequestException,
} from "@nestjs/common";

@Injectable()
export class ValidationPipe implements PipeTransform {
  transform(value: any, metadata: ArgumentMetadata) {
    const { error } = this.handleData(value);
    if (error) {
      throw new BadRequestException("Validation failed");
    }
    const newValue = Object.assign(value, {
      adddress: "北京",
    });
    return newValue; //可以返回修改的数据
  }
  handleData(value) {
    console.log(JSON.stringify(value)); //value可以拿到进行拦截的数据
    return {
      error: false,
    };
  }
}

对应的 app.service 文件,这里的入参 cat 已经是管道 pipe 处理后的数据了

import { Injectable } from "@nestjs/common";

@Injectable()
export class AppService {
  saveList(cat): string {
    return JSON.stringify(cat);
  }
}

11 关联表的使用

11.1 一对一

//目录
database;
---database.module.ts; //module相当于index入口文件,在app.modules中引入import
---database.provider.ts; //初始化数据库,并且初始化数据库model的表关系
---models;
------article.ts; //定义表模型
------location.ts; //定义表模型
------index.ts;

首先 article.ts 文件

import {Sequelize, Model, DataTypes} from 'sequelize';

class ArticleInfo extends Model {
	public readonly id!: number;
	public readonly user_id: number;
	public readonly title: string;
	public readonly full_name: string;
	public readonly other_name: string;
}

const defineArticleInfo = (db: Sequelize): void => {
	ArticleInfo.init(
		{
			id: {
				type: new DataTypes.INTEGER().UNSIGNED,
				primaryKey: true,
				autoIncrement: true,
			},
			user_id: {
				type: DataTypes.INTEGER,
				allowNull: false,
			},
			title: {
				type: DataTypes.STRING,
				allowNull: false,
			},
			full_name: {
				type: DataTypes.STRING,
				allowNull: false,
			},
			other_name: {
				type: DataTypes.STRING,
				allowNull: false,
			},
		},
		{
			sequelize: db,
			tableName: 'article_table',
			timestamps: false,
		}
	);
};

export {ArticleInfo, defineArticleInfo};

类似定了 location.ts 文件

import {Sequelize, Model, DataTypes} from 'sequelize';

class CatInfo extends Model {
	public readonly id!: number;
	public readonly name: string;
	public readonly age: number;
	public readonly address: string;
}

const defineCatInfo = (db: Sequelize): void => {
	CatInfo.init(
		{
			id: {
				type: new DataTypes.INTEGER().UNSIGNED,
				primaryKey: true,
				autoIncrement: true,
			},
			name: {
				type: DataTypes.STRING,
				allowNull: false,
			},
			age: {
				type: DataTypes.INTEGER,
				allowNull: false,
			},
			address: {
				type: DataTypes.STRING,
				allowNull: false,
			},
			type: {
				type: DataTypes.INTEGER,
				allowNull: false,
			},
		},
		{
			sequelize: db,
			tableName: 'location_table',
			timestamps: false,
		}
	);
};

export {CatInfo, defineCatInfo};

然后在 index 中定义了表的关系

import { Sequelize } from "sequelize";

import { CatInfo, defineCatInfo } from "./location";
import { ArticleInfo, defineArticleInfo } from "./article";

const initModels = (sequelize: Sequelize): void => {
  defineCatInfo(sequelize);
  defineArticleInfo(sequelize);
};
/**
 * 两个表:article+location;
 * article中user_id: 表示类型1、2、3
 * location中type[外键:foreignKey],表示1、2、3也就是对应article中user_id
 * 所以 location 是子表;article是父表
 * location.belongsTo(article)
 * article.hasOne(location)
 * eg
 * article表,父亲表:
 * id  |   title     |   user_id
 * 主键 | 文章标题信息  | 唯一类型,表示文章id
 *
 * location表,子表:
 * id  | other   | type
 * 主键 | 其他信息 | 外键,关联父亲表的user_id
 *
 *
 */
const initAssociations = () => {
  //相当于 location.belongsTo(article表,{})
  CatInfo.belongsTo(ArticleInfo, {
    foreignKey: "type",
    targetKey: "user_id",
    as: "leader", //定义查询父表的别名
  });
};
export { CatInfo, ArticleInfo, initModels, initAssociations };

然后 database.provider.ts 文件

import { Sequelize } from "sequelize";
// import {CatInfo, defineCatInfo} from './models/location';
// import {ArticleInfo, defineArticleInfo} from './models/article';

import { CatInfo, ArticleInfo, initModels, initAssociations } from "./models";

export const databaseProviders = [
  {
    provide: "SEQUELIZE",
    useFactory: async () => {
      let dbConfig = {
        host: "127.0.0.1",
        port: 3306,
        username: "root",
        password: "password",
        database: "my_location_demo",
      };

      const sequelize = new Sequelize({
        dialect: "mysql", //要链接数据库的语言
        host: dbConfig.host,
        port: dbConfig.port,
        username: dbConfig.username,
        password: dbConfig.password,
        database: dbConfig.database,
      });
      initModels(sequelize);
      initAssociations();

      return sequelize;
    },
  },
  {
    provide: "CatInfo",
    useValue: CatInfo,
    inject: ["sequelize"],
  },
  {
    provide: "ArticleInfo",
    useValue: ArticleInfo,
    inject: ["sequelize"],
  },
];

对应 database.module.ts 导出

import { Module } from "@nestjs/common";
import { databaseProviders } from "./database.providers";

@Module({
  providers: [...databaseProviders],
  exports: [...databaseProviders],
})
export class DatabaseModule {}

对应主 app.module.ts

import { Module, MiddlewareConsumer, NestModule } from "@nestjs/common";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { CatsController } from "./controller/cats/cats.controller";
import { CatsService } from "./service/cats/cats.service";
import { DatabaseModule } from "./database/database.module";

@Module({
  imports: [DatabaseModule], //这里
  controllers: [AppController, CatsController],
  providers: [AppService, CatsService],
})
export class AppModule {}

最后查询表方法:

async getTypeInfo() {
    return await this.catInfoModel.findAll({
        include: {
            model: this.ArticleInfoModel,
            as: 'leader',//定义查询父表的别名,需要在belongsTo定义表关系的时候定义
        },
    });
}

获取父表中某些属性

async getTypeInfo() {
    return await this.catInfoModel.findAll({
        include: {
            model: this.ArticleInfoModel,
            attributes: ['title', 'fullname'],
            as: 'leader',
        },
    });
}

可以看到使用 子表.belongsTo(父表,{}) ,生成的 json 数据是子表的数据包含了父表的数据

[
  {
    "id": 1,
    "name": "xiaohua",
    "age": 12,
    "address": "shanghai",
    "type": "1",
    "leader": {
      "title": "片段1",
      "fullname": "代码片段1"
    }
  }
]

要想父表包含子表的数据,就需要改变关联表结构,在文件 models/index.ts

const initAssociations = () => {
  ArticleInfo.hasOne(CatInfo, {
    foreignKey: "type",
    sourceKey: "user_id",
    as: "leader",
  });
};

service 调用数据库

async getTypeInfo() {
    return await this.ArticleInfoModel.findAll({
        include: {
            model: this.catInfoModel,
            as: 'leader',
        },
    });
}

生成的数据是,父数据包含了子数据:

[
  {
    "id": 1,
    "user_id": 1,
    "title": "片段1",
    "full_name": "代码片段1",
    "other_name": "代码1",
    "leader": {
      "id": 1,
      "name": "xiaohua",
      "age": 12,
      "address": "shanghai",
      "type": "1"
    }
  }
]

一对多关系

比如 1 个用户对应多个文章,定义

User.HasMany(File, {
	foreignKey: 'creator_id',	// 外键
	sourceKey: 'id',	// 源模型的关联键,默认主键,通常省略
}
/**
 * 两个表:article+location;
 * article中user_id: 表示类型1、2、3
 * location中type[外键:foreignKey],表示1、2、3也就是对应article中user_id
 * 所以 location 是子表;article是父表
 * location.belongsTo(article)
 * article.hasOne(location)
 * eg
 * article表,父亲表:
 * id  |   title     |   user_id
 * 主键 | 文章标题信息  | 唯一类型,表示文章id
 *
 * location表,子表:
 * id  | other   | type
 * 主键 | 其他信息 | 外键,关联父亲表的user_id
 *
 *
 */
const initAssociations = () => {
  ArticleInfo.hasMany(CatInfo, {
    //父亲有多个子表
    foreignKey: "type",
    sourceKey: "user_id",
    as: "leader",
  });
};

生成的数据

[
  {
    "id": 1,
    "user_id": 1,
    "title": "片段1",
    "full_name": "代码片段1",
    "other_name": "代码1",
    "leader": [
      {
        "id": 1,
        "name": "xiaohua",
        "age": 12,
        "address": "shanghai",
        "type": "1"
      },
      {
        "id": 4,
        "name": "lili",
        "age": 22,
        "address": "beijing",
        "type": "1"
      }
    ]
  }
]

从返回的数据中可以看出 hasMany 和 hasOne 的区别,

多对多

Student 对 Lession,中间表是 LessionStudent

student.js

// 与Lessison存在多对多关系,使用belongsToMany()
app.model.Student.belongsToMany(app.model.Lession, {
  through: app.model.LessionStudent,
  foreignKey: "studentId",
  otherKey: "lessionId",
});

Lession.js

Lession 对 Student,中间表是 LessionStudent

Lession.associate = function () {
  // 与student表是多对多关系
  app.model.Lession.belongsToMany(app.model.Student, {
    through: app.model.LessionStudent,
    foreignKey: "lessionId",
    otherKey: "studentId",
  });
};

中间表lession_student

const LessionStudent = app.model.define("lession_student", {
  lessionId: {
    type: INTEGER,
    primaryKey: true,
  },
  studentId: {
    type: INTEGER,
    primaryKey: true,
  },
});

LessionStudent.associate = function () {};

student 与 lession 存在多对多关系,中间表为 lession_student

student 表:classId 表示属于那个班级

id number name classId
'1' '100' '张三' '1'
'2' '120' '里斯' '2'
'3' '150' '昭武' '3'
'4' '90' '阳历' '2'
'5' '124' '州立' '1'

lession 表:

id name
'1' '计算机网络'
'2' 'Java 程序设计'
'3' '软件项目管理'

每个学生对应多个课程
每个课程对应多个学生
所以中间表lession_student

比如课程 id 为 1 的对应的学生有 1+2+3

lessionId studentId
'1' '1'
'1' '2'
'1' '3'
'2' '1'
'2' '4'
'2' '5'
'3' '1'
'3' '2'
'3' '5'

models/student.ts 文件

import {Sequelize, Model, DataTypes} from 'sequelize';

class StudentInfo extends Model {
	public readonly id!: number;
	public readonly number: string;
	public readonly name: string;
	public readonly classId: number;
}

const defineStudentInfo = (db: Sequelize): void => {
	StudentInfo.init(
		{
			id: {
				type: new DataTypes.INTEGER().UNSIGNED,
				primaryKey: true,
				autoIncrement: true,
			},
			number: {
				type: DataTypes.STRING,
				allowNull: false,
			},
			name: {
				type: DataTypes.STRING,
				allowNull: false,
			},
			classId: {
				type: DataTypes.INTEGER,
				allowNull: false,
			},
		},
		{
			sequelize: db,
			tableName: 'students',
			timestamps: false,
		}
	);
};

export {StudentInfo, defineStudentInfo};

models/lession.ts 文件


import {Sequelize, Model, DataTypes} from 'sequelize';

class LessionInfo extends Model {
	public readonly id!: number;
	public readonly name: string;
}

const defineLessionInfo = (db: Sequelize): void => {
	LessionInfo.init(
		{
			id: {
				type: new DataTypes.INTEGER().UNSIGNED,
				primaryKey: true,
				autoIncrement: true,
			},
			name: {
				type: DataTypes.STRING,
				allowNull: false,
			},
		},
		{
			sequelize: db,
			tableName: 'lession',
			timestamps: false,
		}
	);
};

export {LessionInfo, defineLessionInfo};

中间表 lession_students

import {Sequelize, Model, DataTypes} from 'sequelize';

class LessionStudentInfo extends Model {
	public readonly lessionId!: number;
	public readonly studentId: number;
}

const defineLessionStudentInfo = (db: Sequelize): void => {
	LessionStudentInfo.init(
		{
			lessionId: {
				type: new DataTypes.INTEGER().UNSIGNED,
				primaryKey: true,
			},
			studentId: {
				type: new DataTypes.INTEGER().UNSIGNED,
				primaryKey: true,
			},
		},
		{
			sequelize: db,
			tableName: 'lession_student',
			timestamps: false,
		}
	);
};

export {LessionStudentInfo, defineLessionStudentInfo};

然后修改 index.ts

import { Sequelize } from "sequelize";
import { StudentInfo, defineStudentInfo } from "./student";
import { LessionInfo, defineLessionInfo } from "./lession";
import {
  LessionStudentInfo,
  defineLessionStudentInfo,
} from "./lession_student";

const initModels = (sequelize: Sequelize): void => {
  defineStudentInfo(sequelize);
  defineLessionInfo(sequelize);
  defineLessionStudentInfo(sequelize);
};
//定义两个表的关系
const initAssociations = () => {
  StudentInfo.belongsToMany(LessionInfo, {
    through: LessionStudentInfo,
    foreignKey: "studentId",
    otherKey: "lessionId",
  });
  LessionInfo.belongsToMany(StudentInfo, {
    through: LessionStudentInfo,
    foreignKey: "lessionId",
    otherKey: "studentId",
  });
};

export {
  StudentInfo,
  LessionInfo,
  LessionStudentInfo,
  initModels,
  initAssociations,
};

在 database/provider.ts 文件中

import { Sequelize } from "sequelize";

import {
  initModels,
  initAssociations,
  StudentInfo,
  LessionInfo,
  LessionStudentInfo,
} from "./models";

export const databaseProviders = [
  {
    provide: "SEQUELIZE",
    useFactory: async () => {
      let dbConfig = {
        host: "127.0.0.1",
        port: 3306,
        username: "root",
        password: "password",
        database: "my_location_demo",
      };

      const sequelize = new Sequelize({
        dialect: "mysql", //要链接数据库的语言
        host: dbConfig.host,
        port: dbConfig.port,
        username: dbConfig.username,
        password: dbConfig.password,
        database: dbConfig.database,
      });
      initModels(sequelize);
      initAssociations();

      return sequelize;
    },
  },
  {
    provide: "StudentInfo",
    useValue: StudentInfo,
    inject: ["sequelize"],
  },
  {
    provide: "LessionInfo",
    useValue: LessionInfo,
    inject: ["sequelize"],
  },
  {
    provide: "LessionStudentInfo",
    useValue: LessionStudentInfo,
    inject: ["sequelize"],
  },
];

最后在 app.service.ts 文件中

import {Injectable, Inject} from '@nestjs/common';
import { StudentInfo, LessionInfo, LessionStudentInfo} from './database/models';

@Injectable()
export class AppService {
	constructor(
		@Inject('StudentInfo')
		private readonly StudentInfoModel: typeof StudentInfo,
		@Inject('LessionInfo')
		private readonly LessionInfoModel: typeof LessionInfo,
		@Inject('LessionStudentInfo')
		private readonly LessionStudentInfo: typeof LessionInfo
	) {}
	async getTypeInfo() {
		return await this.StudentInfoModel.findAll({
			include: {
				model: this.LessionInfoModel,
			},
		});
	}
}
原文地址:https://www.cnblogs.com/xiaozhumaopao/p/14252321.html