文章目录
概要(Node.js + Koa2 + MySQL + TypeScript)
基础知识
- Node.js:是一个基于Chrome V8引擎的JavaScript运行时,允许在服务器端运行JavaScript代码。
- Koa2:是一个轻量级的Node.js web框架,它使用async/await语法来处理异步操作,使得代码更加简洁易读。
- MySQL:是一种流行的关系型数据库管理系统,用于存储和管理数据。
- TypeScript:是JavaScript的超集,它为JavaScript添加了类型系统,提高了代码的可维护性和可靠性。
应用实例
- 初始化项目
- 创建一个新的目录,例如
koa-mysql-ts-app
,并进入该目录。 - 初始化
package.json
文件:
- 创建一个新的目录,例如
bash
npm init -y
- 安装所需的依赖:
bash
npm install koa mysql2 @types/koa @types/mysql2 typescript ts-node-dev --save-dev
- 配置TypeScript
- 在项目根目录下创建
tsconfig.json
文件,并添加以下配置:
- 在项目根目录下创建
json
{
"compilerOptions": {
"target": "ES6",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
- 创建数据库和表
- 使用MySQL客户端(如phpMyAdmin、Navicat等)创建一个数据库,例如
koa_mysql_ts_db
。 - 创建一个表,例如
users
:
- 使用MySQL客户端(如phpMyAdmin、Navicat等)创建一个数据库,例如
sql
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
age INT NOT NULL
);
- 项目结构
- 在项目根目录下创建
src
目录,用于存放TypeScript源代码。 - 在
src
目录下创建以下文件和目录:config
目录:用于存放数据库配置文件。controllers
目录:用于存放处理业务逻辑的控制器文件。routes
目录:用于存放路由文件。app.ts
:主应用文件。
- 在项目根目录下创建
- 配置数据库连接
- 在
src/config
目录下创建db.ts
文件:
- 在
typescript
import mysql from'mysql2';
const connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: 'password',
database: 'koa_mysql_ts_db'
});
connection.connect((err) => {
if (err) {
console.error('Error connecting to MySQL database:', err);
return;
}
console.log('Connected to MySQL database!');
});
export default connection;
- 创建控制器
- 在
src/controllers
目录下创建userController.ts
文件:
- 在
typescript
import { Context } from 'koa';
import connection from '../config/db';
class UserController {
async getUsers(ctx: Context) {
try {
const [rows] = await new Promise<any>((resolve, reject) => {
connection.query('SELECT * FROM users', (err, results) => {
if (err) {
reject(err);
} else {
resolve(results);
}
});
});
ctx.body = rows;
} catch (error) {
ctx.status = 500;
ctx.body = { error: 'Error fetching users' };
}
}
async createUser(ctx: Context) {
const { name, age } = ctx.request.body;
try {
await new Promise<any>((resolve, reject) => {
connection.query(
'INSERT INTO users (name, age) VALUES (?,?)', [name, age],
(err, results) => {
if (err) {
reject(err);
} else {
resolve(results);
}
});
});
ctx.status = 201;
ctx.body = { message: 'User created successfully' };
} catch (error) {
ctx.status = 500;
ctx.body = { error: 'Error creating user' };
}
}
async updateUser(ctx: Context) {
const { id } = ctx.params;
const { name, age } = ctx.request.body;
try {
await new Promise<any>((resolve, reject) => {
connection.query('UPDATE users SET name =?, age =? WHERE id =?',
[name, age, id], (err, results) => {
if (err) {
reject(err);
} else {
resolve(results);
}
});
});
ctx.status = 200;
ctx.body = { message: 'User updated successfully' };
} catch (error) {
ctx.status = 500;
ctx.body = { error: 'Error updating user' };
}
}
async deleteUser(ctx: Context) {
const { id } = ctx.params;
try {
await new Promise<any>((resolve, reject) => {
connection.query(
'DELETE FROM users WHERE id =?', [id], (err, results) => {
if (err) {
reject(err);
} else {
resolve(results);
}
});
});
ctx.status = 200;
ctx.body = { message: 'User deleted successfully' };
} catch (error) {
ctx.status = 500;
ctx.body = { error: 'Error deleting user' };
}
}
}
export default new UserController();
- 创建路由
- 在
src/routes
目录下创建userRoutes.ts
文件:
- 在
typescript
import { Router } from 'koa';
import userController from '../controllers/userController';
const router = new Router();
router.get('/users', userController.getUsers);
router.post('/users', userController.createUser);
router.put('/users/:id', userController.updateUser);
router.delete('/users/:id', userController.deleteUser);
export default router;
- 主应用文件
- 在
src
目录下的app.ts
文件:
- 在
typescript
import Koa from 'koa';
import userRoutes from './routes/userRoutes';
const app = new Koa();
app.use(userRoutes.routes()).use(userRoutes.allowedMethods());
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on port ${port}`);
});
- 启动项目
- 在
package.json
文件中添加启动脚本:
- 在
json
"scripts": {
"dev": "ts - node - dev --respawn --transpileOnly src/app.ts"
}
- 运行以下命令启动项目:
bash
npm run dev
接口列表
- 获取所有用户
- URL :
/users
- Method: GET
- URL :
- 创建用户
- URL :
/users
- Method: POST
- Body:
- URL :
json
{
"name": "John Doe",
"age": 30
}
- 更新用户
- URL :
/users/:id
- Method: PUT
- Body:
- URL :
json
{
"name": "Jane Doe",
"age": 31
}
- 删除用户
- URL :
/users/:id
- Method: DELETE
- URL :
这个示例展示了如何使用Node.js、Koa2、MySQL和TypeScript构建一个简单的增删改查应用。你可以根据实际需求进一步扩展和优化这个应用。
koa2 基础语法及应用
- Koa2基础语法
- 安装与引入
- 首先需要安装
koa
模块,可以使用npm install koa
命令。在TypeScript项目中,还需要安装@types/koa
类型定义文件。 - 在JavaScript文件中引入
koa
如下:
- 首先需要安装
- 安装与引入
javascript
const Koa = require('koa');
const app = new Koa();
- 在TypeScript文件中引入
koa
如下:
typescript
import Koa from 'koa';
const app = new Koa();
- 中间件(Middleware)
- 中间件是Koa的核心概念。它是一个函数,接收两个参数:
ctx
(上下文对象)和next
(下一个中间件函数)。ctx
包含了请求和响应的信息,如ctx.request
(请求对象)和ctx.response
(响应对象),它们分别是http.IncomingMessage
和http.ServerResponse
的增强版。 - 一个简单的中间件示例如下:
- 中间件是Koa的核心概念。它是一个函数,接收两个参数:
javascript
app.use((ctx, next) => {
console.log('This is a middleware');
// 调用下一个中间件
return next();
});
- 中间件的执行顺序是按照添加的顺序来的。可以通过
app.use()
方法添加多个中间件,并且每个中间件都可以对请求和响应进行处理,比如修改请求头、设置响应状态码等。- 路由(Routing)
- 虽然Koa本身没有内置路由功能,但可以使用第三方路由中间件,如
koa - router
。 - 安装
koa - router
:npm install koa - router
。 - 以下是一个简单的路由示例:
- 虽然Koa本身没有内置路由功能,但可以使用第三方路由中间件,如
- 路由(Routing)
javascript
const Router = require('koa - router');
const router = new Router();
// 定义GET请求路由
router.get('/hello', (ctx) => {
ctx.body = 'Hello, World!';
});
// 将路由中间件添加到Koa应用
app.use(router.routes()).use(router.allowedMethods());
- 在这个示例中,定义了一个
GET
请求的路由/hello
,当访问该路径时,会返回Hello, World!
。router.routes()
返回一个中间件函数,用于处理路由匹配,router.allowedMethods()
用于设置允许的请求方法,确保遵循HTTP规范。 - 上下文(Context)
ctx
对象是Koa中非常重要的部分。它包含了许多有用的属性和方法,例如:ctx.request.url
:获取请求的URL。ctx.request.method
:获取请求的方法(如GET
、POST
等)。ctx.response.status
:设置响应的状态码。ctx.response.body
:设置响应的内容。
- 示例:
javascript
app.use((ctx) => {
ctx.response.status = 200;
ctx.response.body = {
message: 'This is the response body',
url: ctx.request.url
};
});
- 这个中间件会将响应状态码设置为
200
,并返回一个包含消息和请求URL的JSON对象作为响应体。
- Koa2基础应用的目的
- 构建Web应用和API服务
- Koa2提供了一个轻量级的框架来构建Web应用和API服务。它的异步处理机制(通过
async/await
)使得处理异步操作(如数据库查询、外部API调用等)更加简洁和高效。例如,可以轻松地构建一个RESTful API,为前端应用提供数据接口。
- Koa2提供了一个轻量级的框架来构建Web应用和API服务。它的异步处理机制(通过
- 中间件架构的灵活性
- 中间件的架构允许开发者通过组合不同的中间件来添加各种功能,如日志记录、身份验证、请求解析、响应格式化等。这种灵活性使得Koa2可以适应各种不同的应用场景。例如,可以添加一个日志中间件来记录每个请求的信息,或者添加一个CORS中间件来处理跨域请求。
- 高性能和简洁的代码风格
- 相比于一些传统的Web框架,Koa2的代码风格更加简洁。它不强制开发者使用特定的模式或结构,同时通过
async/await
可以减少回调地狱的问题,提高代码的可读性和可维护性。其轻量级的设计也有助于提高应用的性能,减少资源占用,适用于构建高性能的Web应用和微服务。
- 相比于一些传统的Web框架,Koa2的代码风格更加简洁。它不强制开发者使用特定的模式或结构,同时通过
- 构建Web应用和API服务
koa2 与 express 的区别
-
中间件的概念相似性
- Koa2和Express中间件的基本作用类似 :它们都是用于处理HTTP请求和响应的函数。在这两个框架中,中间件函数都可以访问请求对象(如
req
或ctx.request
)和响应对象(如res
或ctx.response
),并且能够在请求 - 响应周期中对请求进行预处理、对响应进行后处理,或者执行一些与业务逻辑相关的操作,如验证用户身份、记录日志等。
- Koa2和Express中间件的基本作用类似 :它们都是用于处理HTTP请求和响应的函数。在这两个框架中,中间件函数都可以访问请求对象(如
-
中间件的执行顺序
- Express中间件执行顺序较为直观 :在Express中,中间件是按照添加的顺序依次执行的。当一个中间件调用
next()
函数时,它会将控制权传递给下一个中间件。例如:
- Express中间件执行顺序较为直观 :在Express中,中间件是按照添加的顺序依次执行的。当一个中间件调用
javascript
const express = require('express');
const app = express();
// 第一个中间件
app.use((req, res, next) => {
console.log('First middleware');
next();
});
// 第二个中间件
app.use((req, res, next) => {
console.log('Second middleware');
next();
});
// 路由处理中间件
app.get('/', (req, res) => {
res.send('Hello from Express');
});
app.listen(3000, () => {
console.log('Express server running on port 3000');
});
- 在上述Express示例中,当有请求进入时,会先执行第一个中间件,打印
First middleware
,然后通过next()
调用第二个中间件,打印Second middleware
,最后执行路由处理中间件返回响应。 - Koa2中间件执行顺序更具灵活性(基于
async/await
) :Koa2的中间件也是按照添加顺序执行,但是由于它基于async/await
语法,中间件可以是异步函数。这使得在中间件中处理异步操作更加自然和方便,并且可以更好地控制中间件的执行顺序和流程。例如:
javascript
const Koa = require('koa');
const app = new Koa();
// 第一个中间件
app.use(async (ctx, next) => {
console.log('First middleware');
await next();
console.log('Back to first middleware after next');
});
// 第二个中间件
app.use(async (ctx, next) => {
console.log('Second middleware');
await next();
console.log('Back to second middleware after next');
});
// 路由处理中间件
app.use((ctx) => {
ctx.body = 'Hello from Koa2';
});
app.listen(3000, () => {
console.log('Koa2 server running on port 3000');
});
- 在Koa2示例中,当请求进入时,先执行第一个中间件,打印
First middleware
,然后调用next()
执行第二个中间件,打印Second middleware
。当路由处理中间件执行完返回响应后,执行流程会反向回到第二个中间件的await next()
之后的代码,打印Back to second middleware after next
,最后回到第一个中间件的await next()
之后的代码,打印Back to first middleware after next
。这种执行顺序的灵活性可以让开发者更方便地在中间件中进行后置处理,如清理资源、记录响应相关的日志等。
- 中间件的编写方式和语法糖差异
- Express中间件的编写方式传统一些 :Express中间件通常采用传统的回调函数形式,如
(req, res, next) => {... }
。虽然也支持async/await
,但不是其主要的编写风格。例如,一个简单的Express中间件用于记录请求时间可能如下:
- Express中间件的编写方式传统一些 :Express中间件通常采用传统的回调函数形式,如
javascript
const express = require('express');
const app = express();
// 记录请求时间的中间件
app.use((req, res, next) => {
const startTime = Date.now();
res.on('finish', () => {
const endTime = Date.now();
console.log(`Request took ${endTime - startTime}ms`);
});
next();
});
// 路由处理中间件
app.get('/', (req, res) => {
res.send('Hello from Express');
});
app.listen(3000, () => {
console.log('Express server running on port 3000');
});
- Koa2中间件更倾向于使用
async/await
语法 :Koa2中间件利用async/await
语法,使得代码更加简洁和易读,尤其是在处理异步操作时。例如,同样是记录请求时间的中间件在Koa2中可以写成:
javascript
const Koa = require('koa');
const app = new Koa();
// 记录请求时间的中间件
app.use(async (ctx, next) => {
const startTime = Date.now();
await next();
const endTime = Date.now();
console.log(`Request took ${endTime - startTime}ms`);
});
// 路由处理中间件
app.use((ctx) => {
ctx.body = 'Hello from Koa2';
});
app.listen(3000, () => {
console.log('Koa2 server running on port 3000');
});
- 可以看到,在Koa2的中间件中,通过
async/await
可以更自然地暂停和恢复中间件的执行,等待异步操作完成,而在Express中可能需要更多地使用回调函数或者Promise来处理异步操作。
- 中间件的错误处理机制不同
- Express中间件错误处理通常通过回调函数传递错误 :在Express中,如果一个中间件内部发生错误,可以将错误对象传递给
next
函数,然后在全局错误处理中间件(通过app.use((err, req, res, next) => {... })
)中捕获和处理错误。例如:
- Express中间件错误处理通常通过回调函数传递错误 :在Express中,如果一个中间件内部发生错误,可以将错误对象传递给
javascript
const express = require('express');
const app = express();
// 一个可能出错的中间件
app.use((req, res, next) => {
throw new Error('This is an error in the middleware');
});
// 全局错误处理中间件
app.use((err, req, res, next) => {
console.error(err);
res.status(500).send('Internal Server Error');
});
app.listen(3000, () => {
console.log('Express server running on port 3000');
});
- Koa2中间件错误处理可以利用
async/await
和try - catch
:在Koa2中,由于中间件可以是异步函数,所以可以使用try - catch
块来捕获中间件内部的错误。同时,Koa2应用本身也是一个事件发射器,可以监听error
事件来处理未被中间件捕获的错误。例如:
javascript
const Koa = require('koa');
const app = new Koa();
// 一个可能出错的中间件
app.use(async (ctx, next) => {
try {
throw new Error('This is an error in the middleware');
} catch (error) {
console.error(error);
ctx.status = 500;
ctx.body = 'Internal Server Error';
}
});
// 监听应用的error事件
app.on('error', (err, ctx) => {
console.error('Uncaught error:', err);
});
app.listen(3000, () => {
console.log('Koa2 server running on port 3000');
});