1、介绍

Koa是一个由Express原班人马打造的轻量级、现代化的Node.js Web框架,旨在提供一个更小、更富有表现力且更健壮的基础。它通过创新的“洋葱模型”中间件机制和现代的async/await语法,为构建Web应用和API带来了全新的体验。

2、安装

1、新建一个文件夹 koa-demo

2、在终端下打开此文件夹

3、执行命令

    npm init -y
    npm install koa -S

4、在根目录下创建 index.js 文件
    
    const Koa = require('koa');
    const app = new Koa();

    app.use(async (ctx)=>{
        ctx.body = 'Hello Koa';
    })

    app.listen(4000);

5、在终端运行命令  node .\index.js 可以启动服务

6、访问http://localhost:4000

3、router使用

npm install @koa/router  -S

https://www.npmjs.com/package/@koa/router

访问不同路由,展示内容不同

const Koa = require('koa');
const app = new Koa();

const Router = require('@koa/router');
const router = new Router();

router.get('/',async (ctx)=>{
    ctx.body = 'Hello Koa Router';
})
router.get('/api',async (ctx)=>{
    ctx.body = 'Hello Koa Router API';
})

app.use(router.routes()).use(router.allowedMethods());

app.listen(4000)

4、中间件

中间件(Middleware) 是在请求和响应处理的“管道”中执行的函数,它能够拦截进入应用的请求,执行自定义逻辑,然后再决定是将请求继续传递下去,还是直接返回响应。

先进后出

// middleware.js

const Koa = require("koa");
const app = new Koa();

const middleware1 = function async(ctx,next) {
    console.log("middleware1")
}
const middleware2 = function async(ctx,next) {
    console.log("middleware2");
    next();
    console.log("m2");
}
const middleware3 = function async(ctx,next) {
    console.log("middleware3");
    next();
    console.log("m3");
}
app.use(middleware2);
app.use(middleware3)
app.use(middleware1)
app.listen(4000);

打印顺序:
middleware2
middleware3
middleware1
m3
m2


5、koa-generator 快速生成koa服务的脚手架

// 全局安装
npm install -g koa-generator

// koa2 + 项目名  -》搭建项目
koa2 koa-generator-project
cd .\koa-generator-project
npm install
npm run dev

6、日志

npm install log4js -D
         

const log4js  = require('log4js');
const levels = {
    trace: log4js.levels.TRACE,
    debug: log4js.levels.DEBUG,
    info: log4js.levels.INFO,
    warn: log4js.levels.WARN,
    error: log4js.levels.ERROR,
    fatal: log4js.levels.FATAL
}

log4js.configure({
    appenders: {
        console: { type: 'console' },    
        info: { type: 'file', filename: 'logs/info.log' },
        warn: { type: 'file', filename: 'logs/warn.log' },
        error: { type: 'file', filename: 'logs/error.log' },
        fatal: { type: 'file', filename: 'logs/fatal.log' },
        trace: { type: 'dateFile', filename: 'logs/trace' ,pattern: 'yyyy-MM-dd.log', alwaysIncludePattern: true }   
    },
    categories: {
        default: { appenders: ['console'], level: 'debug' },
        info: { appenders: ['info','console'], level: 'info' },
        warn: { appenders: ['warn','console'], level: 'warn' },
        error: { appenders: ['error','console'], level: 'error' },
        fatal: { appenders: ['fatal','console'], level: 'fatal' },
        trace: { appenders: ['trace','console'], level: 'trace' }
    }
});


exports.debug = (content) => {
    const logger = log4js.getLogger('debug');
    logger.level = levels.debug;
    logger.debug(content);
};

exports.info = (content) => {
    const logger = log4js.getLogger('info');
    logger.level = levels.info;
    logger.info(content);
};

exports.warn = (content) => {
    const logger = log4js.getLogger('warn');
    logger.level = levels.warn;
    logger.warn(content);
};

exports.error = (content) => {
    const logger = log4js.getLogger('error');
    logger.level = levels.error;
    logger.error(content);
};

exports.fatal = (content) => {
    const logger = log4js.getLogger('fatal');
    logger.level = levels.fatal;
    logger.fatal(content);
};

exports.trace = (content) => {
    const logger = log4js.getLogger('trace');
    logger.level = levels.trace;
    logger.trace(content);
};
app.js


const log4js = require('./utils/log4');
log4js.info('app.js start...');
log4js.debug('app.js debug log');
log4js.warn('app.js warn log');
log4js.error('app.js error log');
log4js.fatal('app.js fatal log');
log4js.trace('app.js trace log');

7、MySQL安装及使用

下载地址:https://dev.mysql.com/downloads/installer/

下载完成后按照提示安装,并设置密码

配置环境变量

以管理员身份运行终端

net start  MySQL版本号:启动服务

net stop MySQL版本号:停止服务

mysql -u root -p -h 127.0.0.1  :登录数据库

注意:必须加分号

输入exit可以退出登录

通过命令创建一个数据库:create database 数据库名;

项目中安装mysql包:npm install mysql --save

npm install mysql2 --save

8、sequelize

安装sequelize: npm install sequelize --save  

sequelize:ORM node框架,对SQL查询语句封装,可以用OOP的方式操作数据库;

// db.js
const Sequelize = require('sequelize');
const log4js = require('./log4');
const sequelize = new Sequelize('dev5','root','Admin@123',{
    host: 'localhost', // 数据库地址
    dialect: 'mysql', // 指定使用 MySQL 数据库
    pool: {
        max: 5, // 连接池中最大连接数量
        min: 0, // 连接池中最小连接数量
        acquire: 30000, // 连接最长等待时间,超过这个时间就会抛出错误
        idle: 10000   // 超过10秒没有操作,就会自动断开连接
    }
});
sequelize.authenticate()
    .then(() => {
        log4js.info('数据库连接成功');  
    })
    .catch(err => {
       log4js.error('数据库连接失败:', err);
    });

    sequelize.sync()  // 同步模型到数据库
  module.exports = sequelize;


// 在app.js中引入
require("./utils/db.js");

8.1、模型(model/user)

const db = require('../utils/db');
const Sequelize = require('sequelize');

const moment = require('moment');

const user = db.define('user', {
    id: {
        type: Sequelize.INTEGER(11),
        primaryKey: true,
        autoIncrement: true
    },
    name: {
        type: Sequelize.STRING(255),
        allowNull: false
    },
    age: {
        type: Sequelize.INTEGER,
        defaultValue: 0,
    },
    sex: Sequelize.STRING,
    birth:{
        type: Sequelize.DATE,
        defaultValue: Sequelize.NOW,
        get() {
            // 获取原始值并格式化为字符串
            const rawValue = this.getDataValue('birth');
            return moment(rawValue).format('YYYY-MM-DD HH:mm:ss');
        }
    },
    createdAt: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.NOW,
        get() {
            const rawValue = this.getDataValue('createdAt');
            return moment(rawValue).format('YYYY-MM-DD HH:mm:ss');
        }
    },
    updatedAt: {
        type: Sequelize.DATE,
        defaultValue: Sequelize.NOW,   
        get() {
            const rawValue = this.getDataValue('updatedAt');
            return moment(rawValue).format('YYYY-MM-DD HH:mm:ss');
        }
    },
    addr: Sequelize.STRING,
},{
    freezeTableName: true, // 禁止修改表名
});

module.exports = user;

8.2、控制层(controller/user)

const User = require('../model/user');
const { Op } = require('sequelize');
const log4js = require('../utils/log4');
const listAll = async(ctx) => {
    log4js.info(`查询用户列表:参数-${JSON.stringify(ctx.query)};请求IP-${ctx.ip};请求路径-${ctx.path}`);
    const page = parseInt(ctx.query.page) || 1; // 当前页码,默认为1
    const pageSize = parseInt(ctx.query.pageSize) || 10;
    const name = ctx.query.name || ''; 
    const offset = (page - 1) * pageSize; // 计算偏移量
     // 动态构建 where 条件
    const whereCondition = {};
    if (name) {
        // 姓名模糊匹配,使用 LIKE 操作符
        whereCondition.name = { [Op.like]: `%${name}%` };
    }
    if (ctx.query.sex) {                                  // 性别精确匹配
        whereCondition.sex = ctx.query.sex;
    }
    try{
        // 分页查询
        const result = await User.findAndCountAll({
            where: whereCondition,
            order: [['createdAt', 'DESC']], // 按照创建时间降序排序
            limit: pageSize,
            offset: offset,
            logging: (sql) => log4js.info(`用户列表查询-SQL: ${sql}`)
        });
        ctx.body = {
            code: 200,
            total: result.count, // 总记录数
            data: result.rows,
            page,
            pageSize,
            message: 'success'
        }
    }catch(error){
        log4js.error('查询用户列表失败', error);
        const msg = error.errors[0]
        ctx.body = {
            code: 500,
            message:  msg.value + ' ' + msg.message
        }
    }
    // const data = await User.findAll(); // 查询所有用户
    // ctx.body = {
    //     code: 200,
    //     data
    // }
}

const create = async(ctx) => {
    const params = ctx.request.body;
    try {
        await User.create(params); // 创建用户
         ctx.body = {
            code: 200,
            message: '用户创建成功'
        }
    } catch (error) {
        const msg = error.errors[0]
        ctx.body = {
            code: 500,
            message: '用户创建失败:' + msg.value + ' ' + msg.message
        }
    }
}

const update = async(ctx) => {
    const {id,name,age,sex,birth,addr} = ctx.request.body;
    try {
        await User.update({name,age,sex,birth,addr},{
            where: {id}
        })
        ctx.body = {
            code: 200,
            message: '用户更新成功'
        }
    } catch (error) {
        const msg = error.errors[0]
        ctx.body = {
            code: 500,
            message: '用户更新失败:' + msg.value + ' ' + msg.message
        }
    }
}

const deleteUser = async(ctx) => {
    try {
        const id = ctx.request.body.id;
        await User.destroy({
            where: { id }
        });
        ctx.body = {
            code: 200,
            message: '用户删除成功'
        }
    } catch (error) {
        const msg = error.errors[0]
        ctx.body = {
            code: 500,
            message: '用户删除失败:' + msg.value + ' ' + msg.message
        }
    }
}
module.exports = {
    listAll,
    create,
    update,
    deleteUser
}

8.3、路由(router/user)

const router = require('koa-router')()
const User = require('../controller/user');
router.prefix('/users')

router.get('/', User.listAll)
router.post('/addUser', User.create)
router.post('/updateUser', User.update)
router.post('/deleteUser', User.deleteUser) 

// router.get('/', function (ctx, next) {
//   ctx.body = 'this is a users response!'
// })

// router.get('/bar', function (ctx, next) {
//   ctx.body = 'this is a users/bar response'
// })

module.exports = router

8.4、常用API

  1)连接数据库

const sequelize = new Sequelize('database', 'username', 'password', {
  host: 'localhost',
  dialect: /* one of 'mysql' | 'postgres' | 'sqlite' | 'mariadb' | 'mssql' | 'db2' | 'snowflake' | 'oracle' */
});

  2)测试数据库是否连接成功

sequelize.authenticate()
.then(()=>{
    // 连接成功
})
.catch(()=>{
    // 连接失败
})

 3)模型:数据库中表的抽象

定义:
sequelize.define

同步某个表:
User.sync()

自动同步所有模型:
sequelize.sync() 

删除某个表:
await User.drop();
console.log("用户表已删除!");

删除所有表:
await sequelize.drop();
console.log("所有表已删除!");

创建实例:
User.create

更新实例:
User.update

删除实例:
User.destory

使用提供的主键从表中仅获得一个条目:
findByPk

除非找到一个满足查询参数的结果,否则方法 findOrCreate 将在表中创建一个条目:
findOrCreate

分页查询:
findAndCountAll

原始查询(sql):
sequelize.query

关联:见  https://www.sequelize.cn/core-concepts/assocs

8.5、场景

1、关联模型并查询

const Sequelize = require('sequelize');

const { DataTypes } = Sequelize;

const db = new Sequelize(...); // 连接数据库


// 商品模型 (A表)
const Product = db.define('Product', {
  primaryId: {
    type: DataTypes.INTEGER,
    primaryKey: true,     // 主键
    autoIncrement: true   // 自增
  },
  name: DataTypes.STRING,
  price: DataTypes.FLOAT
}, {
  tableName: 'A'  // 实际表名
});

// 售卖情况模型 (B表)
const Sale = sequelize.define('Sale', {
  id: {
    type: DataTypes.INTEGER,
    primaryKey: true,
    autoIncrement: true
  },
  quantity: DataTypes.INTEGER,
  saleDate: DataTypes.DATE,
  productId: {
    type: DataTypes.INTEGER,
    references: {        // 外键
      model: Product,
      key: 'primaryId'
    }
  }
}, {
  tableName: 'B'
});

// 建立关联
// 一个商品有多个售卖记录
Product.hasMany(Sale, { foreignKey: 'productId' });
// 售卖记录属于一个商品
Sale.belongsTo(Product, { foreignKey: 'productId' });


// 查询商品id为3的商品信息和售卖信息

try {
    const productWithSales = await Product.findByPk(3, {
      attributes :[            // 如果Product信息全量返回,就不配置
         "primaryId",
         [ "name","productName"]  // 将name重命名为productName
      ], 
      include: [{
        model: Sale,
        attributes:["quantity","saleDate"], // 如果Product信息全量返回,就不配置
        required: false   // LEFT OUTER JOIN,即使没有售卖记录也返回商品
        // required: true  // 则只返回有售卖记录的商品(INNER JOIN)
      }]
    });

    console.log(productWithSales.toJSON());
    /* 输出示例:
    * {
    *    primaryId: 3,
    *    name: '手机',
    *    price: 2999,
    *    Sales: [
    *        { id: 1, quantity: 10, saleDate: '2025-01-01', productId: 3 },
    *        { id: 2, quantity: 5, saleDate: '2025-01-02', productId: 3 }
    *    ]
    * }
    */
    
} catch (err) {

}

9、可视化数据库工具 Navicat

10、接口调试统计 apifox

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐