Knex 在 Koa2 中的应用

Knex 在 Koa2 中的应用

Knexjs 文档

项目demo: koa-vue-notes-api

Knex.js是为Postgres,MSSQL,MySQL,MariaDB,SQLite3,Oracle和Amazon Redshift设计的“包含电池”SQL查询构建器,其设计灵活,便于携带并且使用起来非常有趣。它具有传统的节点样式回调以及用于清洁异步流控制的承诺接口,流接口,全功能查询和模式构建器,事务支持(带保存点),连接池 以及不同查询客户和方言之间的标准化响应。

准备工作,环境变量配置

利用 dotenv 模块将根目录下的 .env 文件配置注入到 process.env 中,

1
2
3
4
5
6
7
8
9
// .env
DB_HOST = your_database_address
DB_PORT = 3306
DB_USER = root
DB_PASSWORD = your_password
DB_DATABASE = database_name

NODE_ENV = development
PORT = 4000

在根目录创建 knexfile.js 作为knex 的配置文件,区分不同的开发环境,对配置进行差异化处理。

1
knex init
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
//knexfile.js

//I only want migrations, rollbacks, and seeds to run when the NODE_ENV is specified
//in the knex seed/migrate command. Knex will error out if it is not specified.
if (!process.env.NODE_ENV) { throw new Error('NODE_ENV not set'); }

require('dotenv').config();

module.exports = {
testing: {
client: 'mysql',
debug: false,
connection: {
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE + '_testing',
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
},
migrations: {
directory: './src/db/migrations',
},
seeds: {
directory: './src/db/seeds/dev',
},
},
development: {
client: 'mysql',
debug: false,
connection: {
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE + '_development',
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'

},
migrations: {
directory: './src/db/migrations',
},
seeds: {
directory: './src/db/seeds/dev',
},
},
production: {
client: 'mysql',
debug: false,
connection: {
host: process.env.DB_HOST,
port: process.env.DB_PORT,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE + '_production',
charset: 'utf8mb4',
collate: 'utf8mb4_unicode_ci'
},
migrations: {
directory: './src/db/migrations',
},
}
};

引入配置,初始化 knex

创建 /db/db.js 作为全局的 db 对象,数据的增删改查都通过 db 对象进行

1
2
3
4
var environment = process.env.NODE_ENV || 'development'; // 判断当前的开发环境
var config = require('../../knexfile.js')[environment]; // 根据开发环境读取相应的配置

module.exports = require('knex')(config); // 初始化 knex 并且导出改模块

migrations 和 seeds

migrations

migrations 允许你定义数据库的模块,所以使得数据库升级变得简单。migrations CLI 被包含在 kenx 中,请全局安装 $ npm install knex -g

migrations 依赖于 knexfile,请提前配置好, 创建 /db/migrations 目录。

创建 migrate

knex migrate:make migration_name

修改 /db/migrations/migration_name.js

其中 up 方法在 migrate run 的时候被执行,用户创建表,down 方法在 migrate rallback 被执行,执行表销毁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//I only want migrations, rollbacks, and seeds to run when the NODE_ENV is specified
//in the knex seed/migrate command. Knex will error out if it is not specified.
if (!process.env.NODE_ENV) {
throw new Error('NODE_ENV not set');
}

exports.up = function (knex, Promise) {
return knex.schema.createTable('notes', function (table) {
table.charset('utf8mb4');
table.collate('utf8mb4_unicode_ci');

table.increments('id').primary();
table.integer('userId');
table.string('title');
table.text('content');
table.string('ipAddress');

table.dateTime('updatedAt').defaultTo(knex.raw('NULL ON UPDATE CURRENT_TIMESTAMP'));
table.dateTime('createdAt').notNullable().defaultTo(knex.raw('CURRENT_TIMESTAMP'));
});
};

exports.down = function (knex, Promise) {
//We never want to drop tables in production
if (process.env.NODE_ENV !== 'production') {
return knex.schema.dropTableIfExists('notes');
}
};

初始化数据库表

完成了 migrate,就可以进行创建表工作,执行 migrate 的 up 方法。

1
knex migrate:latest

更新

1
knex migrate:make update migration_name

回滚

1
knex migrate:rollback

⚠️ 以上的命令都可以增加环境变量 例如 NODE_ENV=development knex migrate:latest

seeds

seeds 为种子,即数据的初始化,可以 mock 数据,插入到表中, 创建 /db/seeds/dev (对应不同环境seeds)

创建 seed

1
knex seed:make seed_users

编辑 seed

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//I only want migrations, rollbacks, and seeds to run when the NODE_ENV is specified
//in the knex seed/migrate command. Knex will error out if it is not specified.
if (!process.env.NODE_ENV) {
throw new Error('NODE_ENV not set');
}

//We don't want seeds to run in production
if (process.env.NODE_ENV === 'production') {
throw new Error('Can\'t run seeds in production');
}

const faker = require('faker');
const bcrypt = require('bcrypt');

exports.seed = async function (knex, Promise) {
// 创建10条用户数据
let seedData = [];

for (let i = 0; i < 5; i++) {
let password = 'demopassword';
try {
password = await bcrypt.hash(password, 12);
} catch (error) {
throw new Error('PASSWORD_ENCRIPTION_ERROR');
}

if (i === 0) {
let testUser = {
token: 'qwertyuiop',
firstName: 'DemoFirstName',
lastName: 'DemoLastName',
username: 'demousername',
email: 'demoemail@example.com',
password: password,
};
seedData.push(testUser);
continue;
}

let testUser = {
token: faker.internet.password(),
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
username: faker.internet.userName(),
email: faker.internet.email(),
password: password,
};
seedData.push(testUser);
}

// Deletes ALL existing entries
await knex('users').truncate();
//Insert users
await knex('users').insert(seedData);
};

运行 seed 将数据插入到表中

1
knex seed:run

⚠️ 注意以上命令可以增加环境变量 ,例如 NODE_ENV=development knex seed:run

操作数据库

以上已经完成数据库的准备工作,一切正常则可使用 knex 操作数据库

引入 db

1
import db from '../db/db.js';

查询

1
2
3
4
5
6
// 检查数据库用户是否存在
var [result] = await db('users').where({username: request.username}).count('id as id');


// 检查重复的email
var [result] = await db('users').where({email: request.email}).count('id as id');

增加

1
var [result] = await db('users').insert(request).returning('id');

修改

1
2
3
4
await db('refresh_token').update({
isValid: false,
updateAt: dateFormat(new Date(), 'YYYY-MM-DD HH:mm:ss')
}).where({username: request.username});

删除

1
await db('notes').delete().where({id: this.id});