Deno で DB マイグレーション を試す

前回 Deno での DB アクセスを試してみましたが、引き続き DB 関連を詰めていきたいと考えました。
というわけで、nessieを使った DB マイグレーションを試します。

参考

Nessie 使ってみる

設定ファイルを作成

以下コマンドで、設定ファイルを生成。

1
2
3
deno run -A --unstable https://deno.land/x/nessie/cli.ts init
# => nessie.config.tsが作成される
# deno run -A --unstable https://deno.land/x/nessie/cli.ts init --mode config --dialect mysql とすればmysql向けの内容で作成される

設定ファイルを編集

設定ファイルの初期状態は以下の通り。

nessie.config.ts[編集前]
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
import {
ClientMySQL,
ClientPostgreSQL,
ClientSQLite,
NessieConfig,
} from "https://deno.land/x/nessie@2.0.0/mod.ts";

/** Select one of the supported clients */
// const client = new ClientPostgreSQL({
// database: "nessie",
// hostname: "localhost",
// port: 5432,
// user: "root",
// password: "pwd",
// });

// const client = new ClientMySQL({
// hostname: "localhost",
// port: 3306,
// username: "root",
// // password: "pwd", // uncomment this line for <8
// db: "nessie",
// });

// const client = new ClientSQLite("./sqlite.db");

/** This is the final config object */
const config: NessieConfig = {
client,
migrationFolders: ["./db/migrations"],
seedFolders: ["./db/seeds"],
};

export default config;

以下のように修正。

nessie.config.ts[編集後]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {
ClientMySQL,
NessieConfig,
} from "https://deno.land/x/nessie@2.0.0/mod.ts";

const client = new ClientMySQL({
hostname: "db",
port: 3306,
username: "root",
password: "パスワード",
db: "testdb",
});

const config: NessieConfig = {
client,
migrationFolders: ["./db/migrations"],
seedFolders: ["./db/seeds"],
};

export default config;

マイグレーションファイル作成

以下コマンドで、マイグレーションファイルを作成。

1
2
3
$ deno run -A --unstable https://deno.land/x/nessie@2.0.1/cli.ts make:migration create_users
Check file:///usr/src/app/nessie.config.ts
Created migration /usr/src/app/db/migrations/20210703032855_create_users.ts

作成された 20210703032855_create_users.ts が以下の通り。

20210703032855_create_users.ts[編集前]
1
2
3
4
5
6
7
8
9
10
11
12
13
import {
AbstractMigration,
Info,
ClientMySQL,
} from "https://deno.land/x/nessie@2.0.0/mod.ts";

export default class extends AbstractMigration<ClientMySQL> {
/** Runs on migrate */
async up(info: Info): Promise<void> {}

/** Runs on rollback */
async down(info: Info): Promise<void> {}
}

このままだと、up down に対応する DB への処理が無いので、編集します。
サンプルで .queryArray 使っている箇所、.query にしないといけないので注意。

20210703032855_create_users.ts[編集後]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import {
AbstractMigration,
Info,
ClientMySQL,
} from "https://deno.land/x/nessie@2.0.0/mod.ts";

export default class extends AbstractMigration<ClientMySQL> {
/** Runs on migrate */
async up(info: Info): Promise<void> {
await this.client.query("CREATE TABLE users (id int)");
}

/** Runs on rollback */
async down(info: Info): Promise<void> {
await this.client.query("DROP TABLE users");
}
}

さすがに、さすがにIDだけのテーブルというのもなと考えて改めて修正。

20210703032855_create_users.ts[編集後 再修正]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import {
AbstractMigration,
Info,
ClientMySQL,
} from "https://deno.land/x/nessie@2.0.0/mod.ts";

export default class extends AbstractMigration<ClientMySQL> {
/** Runs on migrate */
async up(info: Info): Promise<void> {
await this.client.query("CREATE TABLE users (id int AUTO_INCREMENT, name varchar(256), INDEX(id))");
}

/** Runs on rollback */
async down(info: Info): Promise<void> {
await this.client.query("DROP TABLE users");
}

マイグレーション実行

用意できたので、マイグレーション実行します。

1
2
3
4
5
6
# マイグレーション
deno run -A --unstable https://deno.land/x/nessie/cli.ts migrate

# ロールバック
deno run -A --unstable https://deno.land/x/nessie/cli.ts rollback

テーブルの内容を確認。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
use testdb;
show tables;
+-------------------+
| Tables_in_testdb |
+-------------------+
| nessie_migrations |
| users |
+-------------------+
2 rows in set (0.00 sec)

show columns from users;
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | MUL | NULL | auto_increment |
| name | varchar(256) | YES | | NULL | |
+-------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

テーブルの作成ができました。

カラムの追加をしてみる

created_at, updated_at, age のカラムを追加してみます。

テーブル作成時と同じく、マイグレーションファイル作成。

1
$ deno run -A --unstable https://deno.land/x/nessie/cli.ts make:migration add_columns_users

マイグレーションファイルを作成。

20210703131822_add_columns_users.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import {
AbstractMigration,
Info,
ClientMySQL,
} from "https://deno.land/x/nessie@2.0.0/mod.ts";

export default class extends AbstractMigration<ClientMySQL> {
/** Runs on migrate */
async up(info: Info): Promise<void> {
await this.client.query(
"ALTER TABLE users ADD(age int default 0, created_at datetime default current_timestamp, updated_at datetime default current_timestamp on update current_timestamp)"
);
}

/** Runs on rollback */
async down(info: Info): Promise<void> {
await this.client.query(
"ALTER TABLE users DROP age, DROP created_at, DROP updated_at"
);
}
}

改めてマイグレーションを実施。

1
2
# マイグレーション
deno run -A --unstable https://deno.land/x/nessie/cli.ts migrate

改めて、テーブルの内容を確認。

1
2
3
4
5
6
7
8
9
10
11
12
13
use testdb;

show columns from users;
+------------+--------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+-------------------+-----------------------------+
| id | int(11) | NO | MUL | NULL | auto_increment |
| name | varchar(256) | YES | | NULL | |
| age | int(11) | YES | | 0 | |
| created_at | datetime | YES | | CURRENT_TIMESTAMP | |
| updated_at | datetime | YES | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
+------------+--------------+------+-----+-------------------+-----------------------------+
5 rows in set (0.00 sec)

created_at, updated_at, age カラムを増やせました。
とてもいい具合です。


マイグレーションができるツールには感謝するばかりです。
Rails でのマイグレーションファイルは、DSL を記述するので SQL を直接書いていくことはありませんでした。
nessie は SQL を直接書く形なので、テーブル操作 SQL を、さっとかけるようにする必要あるなと感じるところです。

sqlを書いていく形だからでしょうが、必ず up down を書く形なのも Rails との違いでした。

ではでは。