Knexあれこれ(雑多メモ)

# はじめに(次の見出しまで飛ばしてOK)

最近仕事に対する楽しみ方を意識するようになり、今のつまらない仕事が続くのも嫌だったので、6月で辞めますと退職連絡を済ませてきた。
コーディングしてる最中はプライベートでも仕事でも楽しいので、コードをびっしり書くような会社に転職したいと思ったが、個人での活動実績はない。
とりあえず就活に繋がる作品を何か作ってみようと思い立ちこれ1本で大体何とか済ませちゃうNode.jsの勉強を1週間前に始めた。
が、DB処理があまり好きになれなかった。

適当に買った参考書に書いてたDB処理は以下のようなもの

```JavaScript:sql.js
connection.query('SELECT * FROM hoge ', function (error, results, fields) { //取得結果に対する処理 });
```
新卒配属された闇プロジェクトのシステム構成がこれに近い形式で実装されていたことが大体の原因ではあるが。

# 便利なnpmパッケージ Knex
PHP/Laravelあたりに言語/フレームワークを変更しようかなとこっそり考えつつ、何かいいnpmパッケージはないものかと探してみた。
[あった(Knex公式)](http://knexjs.org/#Schema-integer)
[記事を書いてくれている方もいた](https://qiita.com/YoshiyukiKato/items/59c9ac742536d706b322)

個人的に良いなと思った点

- チェーン形式で処理を書ける
- マイグレーションにも対応してる



軽く記事を流し読みるするだけでも結構便利に思えたのでリファレンス読みつつ色々試してみた。
で、ちょっと一々リファレンスを見に行くのもあれなのでちょっとメモを残そうと思いこの記事を書き始めた。~~正直公式リファレンスのは読みづらかった~~
割と参考記事とかぶってる点が多いので、多分knex回りを調べてこの記事を開いてくれた方は↑の参考記事を見ることをお勧めします。

## マイグレーションのあれこれメモ(本題)
### 実行前準備

#### 初期構築 マイグレーションをするためのアレコレの準備

```bash:
$ knex init 
```

#### マイグレーション実行に当たる必要となる設定

```JavaScript:
module.exports = {
  // 開発環境の設定(デフォルトで参照される設定 NODE_ENVを書き換えればデフォルトではなくなる)
  development: {
    // データベースの種類
    client: 'mysql', 

    // DB接続設定
    connection: {
      host     : '127.0.0.1',
      user     : 'root',
      password : '',
      database : 'node_app'
    },
    // コネクションプールの設定
    pool: {
      min: 2,
      max: 10
    },
    // マイグレーション設定
    migrations: {
      // マイグレーションファイルの配置先(knexfile.jsからの相対)
      directory:'./db/migrations',
      // マイグレーションを管理するテーブル名 (マイグレーションの実行と同時にDBに作成される)
      tableName: 'knex_migrations'
    }
  },
  // 開発環境とは異なる環境のマイグレーション実行設定定義 (本番環境,検証環境の差別化等)
  staging: { ... },
  production: { ... },
};
```

### マイグレーションファイルの作成
<タイムスタンプ>_<ファイル名>.jsというファイルが、`migrations.directory`で設定したパス配下に配置される

```bash:
$ knex migrate:make <ファイル名>
```

### マイグレーションファイルの中身の設定
細かく解説すると助長になるので、コメント参照

```JavaScript:20200208161057_items.js
// 後述する実行コマンドで呼び出されるメソッド
exports.up = function(knex, Promise) {
  // connection.databaseで設定しているスキーマに引数で渡したテーブルがあるかチェック
  return knex.schema.hasTable('テーブル名')
    // hasTableのチェーンメソッド 判定結果を引数existsに渡して無名関数をコールバック
    .then(function(exists) {
      if (!exists) {
           // 接続先のスキーマに指定した名前でテーブルを作成する
          return knex.schema.createTable('テーブル名', 
            // 作成したテーブルにカラムを作成する
            function(table) {
               // テーブルの要素設定 別途記載
              table.increments('id').primary();
              table.string('name', 100);
              table.integer('price');
          });
      }else{
          return new Error("The table already exists. 2");
      }
  });
};

// 後述する切り戻しコマンドで呼び出されるメソッド
exports.down = function(knex, Promise) {
  return knex.schema.hasTable('items').then(function(exists) {
      if (exists) {
          // 指定したテーブルを削除する
          return knex.schema.dropTable('items');
      }
  });
};

```

### 接続先データベース/スキーマに対して実行するメソッド各種 (使いそうな奴だけ)

```JavaScript:
function(knex, Promise) {
    // テーブル作成メソッド コールバック内でカラム設定を忘れずに
    knex.schema.createTable(tableName, callback)
                         
    // 指定したテーブル名を変更する
    knex.schema.renameTable(from, to)
                         
    // 指定したテーブルを削除する           
    knex.schema.dropTable(tableName)
                         
    // 指定したテーブルが存在しないか確認する 上例のように.then()でチェーンするとスマート
    knex.schema.hasTable(tableName)
    
    // 指定したテーブルが存在すれば、そのテーブルを削除する. has,dropをチェーンしなくて済む
    knex.schema.dropTableIfExists(tableName)
    
    // 指定したテーブルに関する変更処理を実行する
    knex.schema.table(tableName, callback)
};
```

### 接続先のデータベース/スキーマが持つテーブルに対して実行するメソッド各種 
createTableや、table関数のコールバック内で利用する

```JavaScript:
knex.schema.createTable('posts', function(table) {
    // 指定したカラムを削除する
    table.dropColumn(name)
    
    // 指定した複数のカラムを削除
    table.dropColumns(*columns)
    
    // 指定したカラム名を変更
    table.renameColumn(from, to)
    
    // オートインクリメント形式のカラムを追加
    table.increments(name)
    
    // 文字列型のカラムを追加 オプション引数で長さも指定できる
    table.string(name, [length])
    
    // 数値型のカラムを追加
    table.integer(name)
});
```

### 主キーとか外部キーとかindexとかの使い方
カラム設定の関数にチェーンする

```JavaScript:
knex.schema.createTable('posts', function(table) {
    // 主キーの設定
    table.increments(name).primary()
    
    // knexインスタンスがMySQLとPostgreSQLの場合のみ使える
    table.index(columns, [indexName], [indexType])
    
    // 外部キーの設定 table.foreign(カラム名).references('参照テーブル.カラム名')
    table.string(name)
    table.foreign(name).references(table.name) 
});
```


### 実行手順
#### 未実行のマイグレーションファイルのupメソッドを、タイムスタンプの古い順に全ファイル実行

```Bash:
$ knex migrate:latest
```

#### envオプション: 実行対象の環境を指定してマイグレーションを実行する

```Bash:
$ knex migrate:latest --env production
```

#### 直前に実行した全てのマイグレーションファイルのdownメソッドを、タイムスタンプの新しい順に実行する
Latestでバッチ実行したなら、すべてのファイルをタイムスタンプの新しい順に実行する。
Upで個別実行したなら、そのマイグレーションファイルのみを実行する

```Bash:
$ knex migrate:rollback
```

#### allオプション:過去に実行した全てのマイグレーションファイルのdownメソッドを実行する

```Bash:
$ knex migrate:rollback --all
```

#### タイムスタンプが最も古い未実行のマイグレーションファイルのUpメソッドを実行する

```Bash:
$ knex migrate:up 
```

#### マイグレーションファイルを指定してそのUpメソッドを実行する

```Bash:
$ knex migrate:up <ファイル名>.js
```

#### タイムスタンプが最も新しい実行済のマイグレーションファイルのdownメソッドを実行する

```Bash:
$ knex migrate:down
```

#### マイグレーションファイルを指定してそのdownメソッドを実行する

```Bash:
$ knex migrate:down <ファイル名>.js
```

#### マイグレーションファイルを一覧表示する
実行済みのものと未実行のものに分割してリスト表示してくれる

```Bash:
$ knex migrate:list
Using environment: development
Found 1 Completed Migration file/files.
20200208145923_items.js 
Found 1 Pending Migration file/files.
20200208161057_items.js 
```

#### 作成日の異なるマイグレーションファイルがある場合のmigrate:latestの挙動
タイムスタンプの古い方から実行される

```Bash:
/project
|--migration
|  |--20200208161057_items.js
|  |--20200208161542_items.js
```

```Javascript:20200208161057_items.js
exports.up = function(knex, Promise) {
  console.log('items oldest');
}
```

```Javascript:20200208161542_items.js
exports.up = function(knex, Promise) {
  console.log('items lastest');
}
```

```Bash:実行結果
$ knex migrate:latest
Using environment: development
items oldest
items latest
```

#### 作成日の異なるマイグレーションファイルがある場合のmigrate:rollbackの挙動
タイムスタンプの新しい方から実行される

```Bash:
/project
|--migration
|  |--20200208161057_items.js
|  |--20200208161542_items.js
```

```Javascript:20200208161057_items.js
exports.up = function(knex, Promise) {
  console.log('items oldest');
}
```

```Javascript:20200208161542_items.js
exports.up = function(knex, Promise) {
  console.log('items lastest');
}
```

```Bash:実行結果
$ knex migrate:rollback
Using environment: development
items latest
items oldest
```

オプション引数類は一例で上げてるだけになります。詳細は公式へ

#### migrate:latestの後にもう1回migrate:latestをした場合のmigrate:rollbackの挙動
上述した通り、migrate:rollback は直前の1回の実行を戻す
2回に分けた場合は2回実行するか、`--all`オプション を使う


# ぼやき
結構前からあるパッケージなのにknexの日本語記事少ないってことは、
割とNode.jsを書いてる人からは不人気なのだろうか。