Koa2+Mysql快速入门nodejs后端开发

174 阅读5分钟

一. 初始化package.json

  1. 新建一个项目文件夹,在文件夹中启动终端,运行npm init -y ,会帮我们创建一个package.json文件
  2. 在package.json文件里加上一行"type": "module",这样我们的node项目就是遵循ESM规范了
  3. 在package.json的scripts下面加上一行"start": "nodemon index.js",如果之前没安装过nodemon,可以运行npm i -g nodemon全局安装,nodemon会监听项目文件变化并自动热更新,我们不需要每次修改代码后重新启动项目

二. 安装koa并启动http服务

  1. 运行npm i koa
  2. 通过koa创建并启动http服务,这里直接上代码了
// index.js
import koa from 'koa'

const host = 'localhost'
const port = 8080

// 创建一个 Koa 实例
const app = new koa()

/*
   app.use() 方法用于注册中间件
   中间件是处理 http 请求和响应的函数
   当一个请求到达服务器时, 会从第一个中间件开始执行, 直到最后一个中间件
   
   上下文对象 ctx(context) 包含了与当前 http 请求相关的所有信息
   如: http方法、url、请求头、请求体、查询参数等
*/
app.use(async (ctx) => {
  ctx.body = 'Hello, world!' // 响应体
})

// 启动服务器并在指定的 host 和 port 上监听连接请求
app.listen(port, host, () => {
  console.log(`服务器已启动:http://${host}:${port}`)
})

  1. 关于koa中间件和洋葱模型

app.use的函数里除了ctx上下文对象外,还有一个next函数的参数,如果在中间件中调用了next(),那么就会暂停当前中间件的执行,将控制权传递给下一个中间件,直到下一个中间件运行完,才会继续执行next()后面的代码,这就像是剥洋葱一样,处理请求时从外到内一层层的往里面剥(层层解析,验证,处理),然后再从内到外逐层加调料(封装,格式化),最终形成一道精美的料理。

// 在这段代码里,打印的顺序是1 3 5 4 2
app.use(async (ctx, next) => {
  console.log(1)
  await next() // 暂停,执行下一个中间件
  console.log(2)
})

app.use(async (ctx, next) => {
  console.log(3)
  await next()
  console.log(4)
})

app.use(async (ctx, next) => {
  console.log(5)
  ctx.body = 'Hello Koa'
})

三. 安装和配置路由

路由是指根据客户端发送的请求(包括请求method url)来调用与之匹配的请求处理函数

  1. 运行npm i @koa/router安装
  2. 引入router并创建get请求,这里直接贴代码了,里面包括了get请求,query传参和path传参,以及路由分组
import Router from '@koa/router'
import koa from 'koa'

const host = 'localhost'
const port = 8080

// 创建一个 Koa 实例
const app = new koa()

const router = new Router()

// get请求 http://localhost:8080/list
router.get('/list', async (ctx) => {
  const result = [1, 2, 3]
  ctx.body = result
})

// get请求带查询参数 http://localhost:8080/query?name=张三&age=20
router.get('/query', async (ctx) => {
  const { name, age } = ctx.query
  ctx.body = `name: ${name}, age: ${age}`
})

// get请求带路径参数 http://localhost:8080/path/:id/:age
router.get('/path/:id/:age', async (ctx) => {
  const { id, age } = ctx.params
  ctx.body = `id: ${id}, age: ${age}`
})

app.use(router.routes()) //将定义在 router 对象中的路由规则添加到 app 实例中


// 路由分组
const userRouter = new Router({ prefix: '/user' })
// get请求 http://localhost:8080/user/list
userRouter.get('/list', async (ctx) => {
  const result = [
    { name: '张三', age: 20 },
    { name: '李四', age: 25 },
  ]
  ctx.body = result
})

app.use(userRouter.routes())

// 启动服务器并在指定的 host 和 port 上监听连接请求
app.listen(port, host, () => {
  console.log(`服务器已启动:http://${host}:${port}`)
})

四. 处理post请求

  1. 安装post请求的body解析器 npm i @koa/bodyparser,BodyParser用于解析post请求中以json和url编码格式发送的数据
  2. 引入和使用,直接上代码
import BodyParser from '@koa/bodyparser'
app.use(BodyParser()) // 解析post请求的json或url请求体
router.post('/login', async (ctx) => {
  const { username, password } = ctx.request.body
  if (username === 'admin' && password === '123456') {
    ctx.body = 'success'
  } else {
    ctx.body = 'fail'
  }
})

五.允许跨域请求

  1. 安装npm i @koa/cors
  2. 引入和使用,这样就不会出现跨域请求报错了
import Cors from '@koa/cors'
app.use(Cors()) // 允许跨域请求

六. 连接mysql数据库

  1. 安装mysql2库 npm i mysql2
  2. 连接数据库并写一个根据sql的查询函数
import mysql from 'mysql2'

const config = {
  // 服务器地址
  host: 'localhost',
  // 端口
  port: '3306',
  // 数据库名
  database: 'test-db',
  // 用户名
  username: 'root',
  // 密码
  password: 'password',
}

const pool = mysql.createPool({
  host: config.host, // 地址
  port: config.port, // 端口号
  user: config.username, // 用户名
  password: config.password, // 密码
  database: config.database, // 目标数据库
})

const query = (sql) => {
  return new Promise((resolve, reject) => {
    pool.execute(sql, (err, results) => {
      if (err) {
        reject(err)
      }
      resolve(results)
    })
  })
}

export default query
  1. 在接口处使用sql语句调用query函数即可查询数据库
router.get('/list', async (ctx) => {
  const sql = `select * from tb_user`
  const res = await query(sql)
  console.log(res)
  ctx.body = res
})

七. 使用sequelize连接数据库并实现增删改查

上面那段是通过手写sql然后执行的方式操作数据库,这种方式有很多弊端,而且sql一旦写错了后果很严重,接下来会演示通过sequelize这个库去操作数据库

  1. 安装sequelize npm i sequelize
  2. 新建models文件夹,用来存放数据库模型相关的文件
  3. 在models文件夹下面新建user.js,用来定义用户表,user.js中写入如下代码
// models/user.js
// 定义了一个tb_user的数据模型,有id username password name这四个字段,对应数据库里的tb_user表
export default (sequelize, DataTypes) => {
  return sequelize.define(
    'tb_user',
    {
      id: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true }, // 自增主键
      username: { type: DataTypes.STRING, allowNull: false },
      password: { type: DataTypes.STRING, allowNull: false },
      name: { type: DataTypes.STRING, allowNull: false },
    },
    { tableName: 'tb_user' }
  )
}
  1. 在models文件夹下面新建index.js文件,在里面配置sequelize连接
// models/index.js
import { Sequelize } from 'sequelize'

const database = 'test-db'
const username = 'test-db'
const password = '123456'

const sequelize = new Sequelize(database, username, password, {
  host: 'localhost',
  dialect: 'mysql',
  logging: false,
})

const db = {}

db.Sequelize = Sequelize
db.sequelize = sequelize

// 加载模型
db.User = (await import('./user.js')).default(sequelize, Sequelize)

export default db
  1. 在app.js中连接并同步数据表
// app.js
try {
  // 同步数据表,上面定义的tb_user表和表字段会同步到mysql数据库的tb_user表中
  await db.sequelize.sync({ alter: true }); 
  
  // 连接数据库
  await db.sequelize.authenticate();

  console.log('所有模型已成功同步,表已重新创建,数据库已连接成功');
} catch (err) {
  console.error('数据库连接失败:', err);
}
  1. 通过sequelize实现增删改查
import db from './models/index.js';
// 创建用户
router.post('/users', async (ctx) => {
  const { username, password, name } = ctx.request.body;
  const user = await db.User.create({ username, password, name });
  ctx.body = user;
});

// 获取所有用户
router.get('/users', async (ctx) => {
  const users = await db.User.findAll();
  ctx.body = users;
});

// 获取单个用户
router.get('/users/:id', async (ctx) => {
  const user = await db.User.findByPk(ctx.params.id);
  if (user) {
    ctx.body = user;
  } else {
    ctx.status = 404;
    ctx.body = { message: 'User not found' };
  }
});

// 更新用户
router.put('/users/:id', async (ctx) => {
  const { username, password, name } = ctx.request.body;
  const updated = await db.User.update({ username, password, name }, {
    where: { id: ctx.params.id }
  });
  if (updated[0] > 0) {
    ctx.body = { message: 'User updated' };
  } else {
    ctx.status = 404;
    ctx.body = { message: 'User not found' };
  }
});

// 删除用户
router.delete('/users/:id', async (ctx) => {
  const deleted = await db.User.destroy({
    where: { id: ctx.params.id }
  });
  if (deleted > 0) {
    ctx.body = { message: 'User deleted' };
  } else {
    ctx.status = 404;
    ctx.body = { message: 'User not found' };
  }
});