使用koa、typescript、mysql、sequelize进行后台API开发
使用Koa与typescript、mysql、sequelize组合进行服务端api开发,主要案例有:数据库表生成,图片上传,热启动,excel表数据导入与导出
文章目录
运行命令
npm init
tsc --init
需要安装的依赖包:
// 必选npm包
yarn add koa koa-router
yarn add @types/koa @types/koa-router
yarn add mysql2 // mysql驱动
yarn add koa-logger koa-static // 请求日志与静态文件
yarn add koa-router-ts // 路由控制器
yarn add moment // 时间格式化
yarn add typescript // typescript依赖
yarn add ts-node
yarn add nodemon dotenv // 热启动依赖
yarn add sequelize-typescript sequelize // sequelize依赖
yarn add @types/node @types/validator reflect-metadata // 使用sequelize-typescript必须
yarn add koa-bodyparser koa2-cors // 跨域与post请求解析
// 可选npm包
yarn add koa-json // json格式
yarn add @koa/multer multer // 实现文件上传安装
yarn add uuid // 随机id
npm install bcrypt // 密码加密
npm install jsonwebtoken // 生成token
yarn add joi // 校验
日志中间件也可以使用
yarn add koa-pino-logger
第一步 配置热启动项目
1.配置tsconfig.json文件
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"module": "commonjs",
"esModuleInterop": true,
"target": "ES6",
"moduleResolution": "node",
"sourceMap": true,
"outDir": "dist" // 编译文件存放地址
},
"lib": ["es2015"]
}
2.创建并配置nodemon.json文件
{
"watch": ["src"],
"ignore": ["build", ".git", "node_modules"],
"exec": "ts-node -r dotenv/config ./src/index.ts",
"ext": "js,json,ts,tsx"
}
3.修改package.json文件配置script
"scripts": {
"build": "tsc",
"dev": "nodemon --watch",
"start": "node ./dist/index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
通过上述步骤我们就配置成功了热启动项目,能够实时监听项目修改
当我们需要本地运行开发项目时,通过运行命令:
// 本地开发运行项目
yarn dev
或
npm run dev
// 需要在服务器上运行项目,进行编译,会生成一个dist文件夹,文件夹中就是编译后的产物;然后将编译后的产物放置到服务器上进行运行即可
yarn build
或
npm run build
第二步 进行项目开发
1.在项目根目录下创建src文件夹,并分别创建其子文件夹controllers、models、services、utils、static、middleware等文件夹以及index.ts文件
constrollers: 控制器管理文件目录,用于实现路由请求api
models: 数据库模板文件目录
services: 数据库操作服务文件目录
middleware: 自定义中间件文件目录
static: 静态文件目录(img、file)
index.ts: 项目的入口文件
2.配置项目入口文件index.ts
import Koa from "koa";
import statics from "koa-static"; // 静态文件
import {
loadControllers } from "koa-router-ts"; // 引入路由控制器
import Logger from "koa-logger"; // 日志
import moment from "moment"; // 时间格式化
import bodyParser from "koa-bodyparser"; // post请求
import path from "path";
import cors from "koa2-cors"; // 跨域处理
// init db models
import "./models"; // 加载数据库模板文件
const staticPath = "./static"; // 静态文件地址
const logger = new Logger(str => {
// 日志时间格式化处理
console.log(moment().format("YYYY-MM-DD HH:mm:ss") + str);
});
// 实例化koa
const app = new Koa();
// 加载中间件
app.use(bodyParser());
app.use(logger);
app.use(statics(path.join(__dirname, staticPath)));
// 实例化路由
const router = loadControllers(path.join(__dirname, "controllers"), {
recurse: true,
});
// 设置路由api前缀
router.prefix("/api/v1");
app.use(router.routes()).use(router.allowedMethods());
// 设置跨域
app.use(
cors({
origin: (ctx: any) => {
if (ctx.url === "/test") {
return "*";
}
return "http:localhost:8000"; // 允许http:localhost:8000请求跨域
},
maxAge: 5,
credentials: true,
allowMethods: ["GET", "POST", "PUT", "DELETE"],
allowHeaders: ["Content-Type", "Authorization", "Accept"],
exposeHeaders: ["WWW-Authenticate", "Server-Authorization"],
})
);
const PORT = process.env.PORT || 8044;
app.listen(PORT);
3.配置连接数据库以及数据库模板
首先,在models文件夹下创建index.ts文件,配置连接数据库
import {
Sequelize } from "sequelize-typescript";
const sequelize = new Sequelize({
database: "dataapi", // 数据库名称
dialect: "mysql", // 使用的数据库类型
username: "root", // 用户名
password: "123456", // 密码
port: 3306, // 端口号
models: [`${
__dirname}/**/*.model.ts`, `${
__dirname}/**/*.model.js`], // 数据库模板存放地址
});
sequelize.sync({
force: false });
// 导出相应模块
export {
sequelize };
export {
Sequelize };
export default sequelize.models;
其次,创建相应的数据库模板(以user模板表为例)
在models文件夹中创建user子文件夹,并在该文件夹下创建user.model.ts文件.
// 引入sequelize-typescript相关构造器
import {
Table, Column, DataType, Model } from "sequelize-typescript";
@Table({
tableName: "user_details", // 表名称
underscored: true, // 使用下划线
timestamps: true, // 是否自动生成创建以及更新时间字段
indexes: [ // 索引
{
unique: false, // 是否唯一
fields: ["name"],
},
],
})
export default class UserDetails extends Model<UserDetails> {
@Column({
type: DataType.UUID, // 类型
defaultValue: DataType.UUIDV4, // 默认值
primaryKey: true, // 是否主键
})
id!: string; // id 字段
@Column({
type: DataType.STRING(255),
comment: "名称", // 描述
})
name!: string;
@Column({
type: DataType.STRING(20),
comment: "性别",
})
gender!: string;
@Column({
type: DataType.INTEGER,
comment: "年龄",
})
age: number;
@Column({
comment: "出身年月",
})
birth_date: Date;
@Column({
type: DataType.STRING(255),
comment: "家庭地址",
})
address!: string;
@Column({
type: DataType.INTEGER,
comment: "手机号码",
})
phone_number: number;
@Column({
type: DataType.STRING(50),
comment: "邮箱",
})
email!: string;
@Column({
type: DataType.STRING(125),
comment: "职业",
})
occupation!: string;
@Column({
type: DataType.STRING(255),
comment: "公司",
})
company!: string;
@Column({
type: DataType.STRING(1024),
comment: "爱好",
})
hobby!: string;
}
4.根据模板创建相应服务文件操作数据库(以user模板为例)
在utils文件夹下创建response.ts文件,该文件用于实现自定义数据返回形式
// 定义接口
export interface IResponse {
status: string;
message: string;
getHttpStatusCode(): number;
}
// 定义类SuccessResponse ,用于处理成功类型
export class SuccessResponse implements IResponse {
status: string;
message: string;
data: any;
constructor(data: any, message: string = "") {
this.status = "ok";
this.message = message;
this.data = data;
}
/**
* 获取 HTTP status code
*/
public getHttpStatusCode(): number {
return 200;
}
}
// 定义类ErrorResponse,用于处理失败类型
export class ErrorResponse implements IResponse {
status: string;
message: string;
code: number;
constructor(message: string, code: number) {
this.status = "error";
this.message = message;
this.code = code;
}
public getHttpStatusCode(): number {
return this.code;
}
}
在services文件夹下创建user子文件夹,并在该user文件夹中创建user.ts文件
// 引入自定义响应形式
import {
SuccessResponse,
ErrorResponse,
IResponse,
} from "../../utils/response";
// 引入表模板
import UserDetails from "../../models/user/user.model";
export default class UserService {
/**
* 根据id查询
* @param ctx ctx
* @param id id
*/
async findByPK(ctx: any, id: string): Promise<IResponse> {
try {
const result = await UserDetails.findByPk(id);
if (!result) {
return new ErrorResponse("找不到该数据", 404);
}
return new SuccessResponse(result, "成功获取数据");
} catch (error) {
ctx.throw(500, error.message);
}
}
/**
* 获取全部数据
* @param ctx ctx
*/
async findAll(ctx: any): Promise<IResponse> {
try {
const result = await UserDetails.findAll();
return new SuccessResponse(result, "成功获取数据");
} catch (error) {
ctx.throw(500, error.message);
}
}
/**
* 添加数据
* @param form 表单
* @param ctx ctx
*/
async create(ctx: any, form: any): Promise<IResponse> {
try {
const result = await UserDetails.create(form);
return new SuccessResponse(result, "成功创建数据");
} catch (error) {
ctx.throw(500, error.message);
}
}
/**
* 更新数据
* @param form 表单
* @param id id
* @param ctx ctx
*/
async update(ctx: any, form: any, id: string): Promise<IResponse> {
try {
const result = await UserDetails.update(form, {
where: {
id,
},
});
return new SuccessResponse(result, "成功更新数据");
} catch (error) {
ctx.throw(500, error.message);
}
}
/**
* 删除数据
* @param id id
* @param ctx ctx
*/
async delete(ctx: any, id: string): Promise<IResponse> {
try {
const result = await UserDetails.destroy({
where: {
id,
},
});
return new SuccessResponse(result, "成功删除数据");
} catch (error) {
ctx.throw(400, error.message);
}
}
}
5.根据服务创建相应的路由请求(以user服务为例)
在constrollers文件夹中创建user子文件夹,并在user子文件夹下创建user.ts文件
// 引入路由构造器
import {
Controller, Post, Get, Put, Delete } from "koa-router-ts";
// 引入操作
import UserService from "../../services/user/user_details";
// 创建服务实例
const service = new U