node.js(TypeScript) でMySQL

node.jsでMySqlのDBを操作する機会があったので備忘録としてまとめます。

## まずTypeScriptのコンパイル環境をつくる

完全に好みが分かれると思いますが、TypeScriptで書きたくなりました。

```bash
npm i -g typescript
npm i -g tsc
```

### 下準備

DB操作は非同期処理になります。
TypeScriptで `async/await`が使えるようにしておきます。

```json:tsconfig.json
{
  "compilerOptions": {
  "lib": ["es2015"]
}

```

## mysqljs
nodeでのMySQL操作はこちらを利用しました。
https://github.com/mysqljs/mysql

```bash
npm i --save mysql @types/mysql
```

## ハマりポイント
###  非同期処理
基本的なDB操作は公式のとおりなので割愛しますが、
複数の更新操作をする場合、前の更新が終わってから次の更新を行う必要があると思います。
先ほど下準備した `async/await`の出番、と思ってこう書きました。

```typescript:index.ts
import * as mysql from 'mysql';

// DB接続情報を定義
export interface DbConfig {
    host: string;
    user: string;
    password: string;
    database: string;
}
const dbConfig: DbConfig = {
    host: 'HOST_NAME',
    user:'USER_NAME',
    password:'PW',
    database:'DB'
}; 
// 接続
const pool = mysql.createPool(dbConfig);

/** INSERT用のasync関数 */
async function insertRecord(tableName: string, data: any) {
    try {
        const sql: string = mysql.format(`INSERT INTO ${tableName} SET ?`, data);
        return await pool.query(sql) as any;
    } catch (err) {
        pool.end();
        throw new Error(err);
    }
}

(async () => {
  try{
  // 挿入するレコードを準備
   const insertRecord: any = {
   name: 'hoge'
  };

 // 挿入処理
  const result =  await insertRecord('TABLE_NAME', insertRecord);

 // 挿入結果のレコードIDをコンソール出力
  console.log(result.insertId);
  pool.end();

 } catch (err) {
   pool.end();
   throw  new Error(err);    
 }
})();
```

#### でも結果は、レコードは挿入されますが、コンソール出力はされませんでした。

---

### 原因

#### mysqljsライブラリの`pool.query`がPromiseを返してくれない

`pool.query` はPromiseを返してくれないので、非同期処理が終わっても待ち状態のままになります。

下の3行追加することで解決しました。

```typescript:index.ts
import * as util from 'util';

const pool = mysql.createPool(dbConfig);

// @ts-ignore
pool.query = util.promisify(pool.query);

```

`pool.query = util.promisify(pool.query);` とすることで、 `Promise` を返すように上書きしています。
※TypeScriptだとコンパイルのときに型のエラーがでたのでスルーしてもらうために `@ts-ignore` を1行上に追加しています。

### そのほかのハマりポイント

#### TypeScriptのビルド
すごくしょうもないですが、 `index.ts`　にいろいろ書いて、下記のようにファイル名を指定してビルドを実行すると`tsconfig.json`の内容が反映されません。

```bash
# ファイル指定はだめ
tsc index.ts

# OK
tsc

```

#### forEachはPromiseをみてくれない
forEachの中でasync functionをつかっても、Promiseのリターンを待たずにつぎにいってしまいます。
forEach自体にasyncとかつけても変わりません。

おとなしくfor文を使いました。


## 最後に

nodeでMySQLですが、挿入したレコードのidを取得して別のレコードのカラムを更新するのが楽にできて便利でした。

トランザクションロールバックの処理まで記事にしようと思いましたが、長くなったので書いたコードだけ上げておきます。

```typescript:index.ts
/**
 * INSERT,UPDATE,DELETEなど、
 * エラー時のTransaction Rollbackを必要とするsqlの発行に使用 */
async function editRecordOfTransactionRollback(sql: string) {
    console.log(sql);
    return await new Promise((resolve => {
        pool.getConnection((err, connection) => {
            if (err) {
                connection.release();
                pool.end();
                throw err;
            }
            connection.beginTransaction(err1 => {
                if (err1) {
                    connection.release();
                    pool.end();
                    throw err1;
                }
                connection.query(sql, (error, results) => {
                    if (error) {
                        connection.rollback(() => {
                            connection.release();
                            pool.end();
                            throw error;
                        });
                    } else {
                        connection.commit((err2 => {
                            if (err2) {
                                connection.rollback(() => {
                                    connection.release();
                                    pool.end();
                                    throw err2;
                                });
                            } else {
                                connection.release();
                                resolve(results);
                            }
                        }));
                    }
                });
            });
        });
    })) as Promise<any>;
}

```