Chapter 14

knex.jsでデータベースを操作しよう

前章ではmysqlモジュールを利用してデータベースを操作しました。
本章では、knex.jsというクエリビルダを導入し、データベースの操作部分を変更します。
クエリビルダとは、SQLを簡単に組むことはできる機能のことです。
mysqlモジュールを使ったデータベース操作では、SQLを丸々書く必要がありましたが、複雑な操作を行う場合はSQLも複雑になるため、手間がかかり、ミスも増えます。
クエリビルダは丸々SQLを書くよりも分かりやすく、簡単な記述でSQLを発行できるため、次章からデータベース操作が増える前にknex.jsに切り替えます。

knex.jsの環境構築をしよう

knex.jsは外部モジュールのため、mysqlモジュールと同様にnpmでインストールする必要があります。
コンソールでnode_studyフォルダ内にあるtodoappフォルダに移動してください。

以下コマンドでknex.jsモジュールをインストールしましょう。

npm install knex

次に、以下コマンドでknex.jsの接続設定ファイルを作成しましょう。

npx knex init

todoappフォルダ内に作成されたknexfile.jsというファイルをVisual Studio Codeで開いてください。

mysqlモジュールで接続情報を定義したように、knex.jsでもこのknexfile.jsで接続情報を定義します。

既にサンプルの設定が用意されていますが、不要な項目や今回の環境とは異なる設定がされているため、このファイル内のコードを全て削除して空にしてください。

空になったknexfile.jsに以下をコピー&ペーストしてください。

// Update with your config settings.

module.exports = {

  development: {
    client: "mysql",
    connection: {
      database: "todo_app",
      user: "root",
      password: "[事前準備で設定したrootユーザのパスワード]",
    },
    pool: {
      min: 2,
      max: 10
    },
  },

  staging: {
    client: "mysql",
    connection: {
      database: "todo_app",
      user: "root",
      password: "[事前準備で設定したrootユーザのパスワード]",
    },
    pool: {
      min: 2,
      max: 10
    },
  },

  production: {
    client: "mysql",
    connection: {
      database: "todo_app",
      user: "root",
      password: "[事前準備で設定したrootユーザのパスワード]",
    },
    pool: {
      min: 2,
      max: 10
    },
  }

};

[事前準備で設定したrootユーザのパスワード]の部分をご自身のものに置き換えてください。
事前準備で設定したrootユーザのパスワードがhogeの場合は、password: "hoge",となります。

設定はオブジェクトで定義します。

同じ設定がdevelopment、staging、productionの3つ分ありますが、これは環境ごとに設定を用意しているためです。
アプリケーションは一般的に、開発環境で開発し、良きタイミングで開発内容をステージング環境に適用して動作確認を行い、問題があれば再度開発環境で修正、なければ本番環境に適用してリリースという流れで更新されていきます。
development、staging、productionはこの開発環境、ステージング環境、本番環境を表しています。

それでは各設定項目について説明します。

  • client
    使用するデータベース用のモジュールを指定します。
    今回はmysqlモジュールを使用するため、mysqlとなります。

  • connection
    接続情報を設定します。

    • database
      使用するデータベース名を指定します。
      今回はtodo_appとなります。

    • user
      接続に使用するユーザを指定します。
      今回はrootとなります。

    • password
      接続に使用するユーザのパスワードを指定します。
      今回はrootユーザを使用するため、rootユーザのパスワードとなります。

  • pool
    コネクションプールを設定します。
    コネクションプールとは、データベースへの接続を保持して再利用する機能のことをいいます。

    • min
      保持する接続数の最大限度を指定します。
    • max
      保持する接続数の最小限度を指定します。

それでは、knexモジュールと設定を読み込むためのコードを記述し、これをknexを利用したいファイルから読み込めるようにします。

todoappフォルダ内にdbフォルダを作成してください。

次に、Visual Studio Codeを開き、新規ファイルを作成し、knex.jsというファイル名でdbフォルダに保存しましょう。

フォルダ構成は以下のようになります。

任意の場所
└todo-app
 └db
  └knex.js
 ...
 ...
 ...

knex.jsファイルを編集していきます。

まず、環境を指定して定数に格納しましょう。
knexの設定では、後述のとおりdevelopment、staging、productionという3つの環境に応じたキー名が用意されていました。
今回は開発環境を表すdevelopmentを指定します。

knex.jsファイルの先頭に以下を記述してください。

const environment = "development";

次に、developmentの設定を読み込むコードを書きます。
const environment = "development";の下に以下を記述してください。

const config = require("../knexfile.js")[environment];

requre()関数でknexの設定ファイルであるknexfile.jsを読み込み、さらに[environment]とすることでで、developmentの設定を読み込んでいます。

それではknexモジュールと設定が格納されている定数configを同時に読み込むコードを書きましょう。
const config = require("../knexfile.js")[environment];の下に以下を記述してください。

const knex = require("knex")(config);

これで定数knexを読み込めば、knexモジュールとdevelopment用の設定を読み込むことができるようになりました。

しかし、このままでは外部ファイルからこの定数knexを読み込むことはできません。
module.exportsを利用し、外部に公開します。
const knex = require("knex")(config);の下に1行空け、以下を記述してください。

module.exports = knex;

右辺に外部から読み込みたいもの (今回の場合は定数knex) を指定します。
これで、定数knexを読み込むことでどのファイルでもknexを利用できるようになりました。

以上でknex.jsの環境構築は完了です。

knex.jsを利用したデータベース操作に変更しよう

knex.jsを利用する準備が整ったので、index.jsで行っているデータベース操作を knex.jsを利用したものに変更しましょう。

Visual Studio Codeでroutesフォルダ内にあるindex.jsを開いてください。

まず、knex.jsモジュールと設定が格納されている定数knexを読み込みます。
const mysql = require('mysql');の上に以下を記述してください。

const knex = require('../db/knex');

GETリクエスト時の処理内にある、tasksテーブルから全レコードを取得する操作をknex.jsで行ってみましょう。

tasksテーブルから全レコードを取得するSQLはselect * from tasks;ですが、これをknex.jsで書くと次のようになります。

knex("tasks")
  .select("*")

このように、SQLで書くよりも分かりやすく、見やすい書き方ができます。
特にSQLが複雑になるほどその差が分かります。

("tasks")でどのテーブルを操作するか、("*")でどのカラムのデータを取得するかを指定しています。
全てのカラムのデータを取得したい場合は*を指定します。
knex.jsはここからselect * from tasks;というSQLを発行し、実行してくれます。

また、.then()でデータベース操作が正常に行われた場合のそれに続く処理を、.catch()でデータベース操作でエラーが発生した場合のそれに続く処理を書くことができます。

それでは、router.get()の処理を以下に変更してください。

router.get('/', function (req, res, next) {
  knex("tasks")
    .select("*")
    .then(function (results) {
      console.log(results);
      res.render('index', {
        title: 'ToDo App',
        todos: results,
      });
    })
    .catch(function (err) {
      console.error(err);
      res.render('index', {
        title: 'ToDo App',
      });
    });
});

.then()内では、関数function (results) {}によって正常時の処理を実装しています。
引数resultsにはデータベース操作の結果、今回であればtasksテーブルから取得したデータが格納されています。
また、.catch()内では、関数function (err) {}によってエラー時の処理を実装しています。
引数errにはデータベース操作中に発生したエラー内容が格納されています。

それでは、確認してみましょう。
npm startでサーバを起動し、chromeでhttp://localhost:3000/にアクセスしてください。

前章で追加したToDoタスクが問題なく表示されました。

ToDoタスクの追加にもknex.jsを利用しましょう。
router.post()の処理を以下に変更してください。

router.post('/', function (req, res, next) {
  const todo = req.body.add;
  knex("tasks")
    .insert({user_id: 1, content: todo})
    .then(function () {
      res.redirect('/')
    })
    .catch(function (err) {
      console.error(err);
      res.render('index', {
        title: 'ToDo App',
      });
    });
});

knex.jsではinsert文を.insert()で表します。
また、引数にはオブジェクト形式でキーがカラム名、値が追加するデータとなるように指定します。

それでは、こちらも確認してみましょう。
ctrl+cでサーバを停止し、npm startでサーバを起動後、chromeでhttp://localhost:3000/にアクセスしてください。

新たにToDoタスクを追加してみましょう。

問題なく追加できました。

これで、ToDoタスクの取得、ToDoタスクの追加共にknex.jsを利用できました。
このSQLをknex.jsではどのように書くのかを調べる際には、knex.jsの公式ドキュメント (http://knexjs.org/) を参考にしてみてください。