Dockerを使ったFastifyとMySQLの環境構築とCRUD操作の方法

FastifyにおけるDockerを使ったFastifyとMySQLの環境構築する方法を解説します。

概要

Node.js ベースの Web フレームワークである Fastify のDockerを使ったFastifyとMySQLの環境構築する方法について解説します。

ここでは、以下バージョンを使用した、Fastifyの環境を構築する方法を説明します。

Fastify v4.19.2
nodejs v19.7.0

また、今回作成するコードは全て、GitHubに掲載しています。

なお本記事では、Dockerを使用するため、Dockerをインストールしてください。公式のDockerウェブサイトから、対応するオペレーティングシステムに合わせたDockerのインストーラーをダウンロードし、インストールを行ってください。

Dockerを使ったFastifyとMySQLの環境構築する方法

プロジェクトディレクトリの作成

まず、FastifyプロジェクトとMySQLサーバーのために新しいディレクトリを作成します。コマンドラインで以下のコマンドを実行してください:

mkdir fastify-docker-mysql

プロジェクトディレクトリへの移動

プロジェクトディレクトリに移動します。以下のコマンドを実行してください:

cd fastify-docker-mysql

プロジェクトの初期化

Fastifyプロジェクトを初期化するために、以下のコマンドを実行します。

npm init -y

必要なパッケージのインストール

Fastifyを起動するために必要なパッケージをインストールします。

npm install fastify@4.19.2 mysql2

Docker Compose ファイルの作成

Docker Composeを使って、FastifyアプリケーションとMySQLのDockerコンテナを一緒に起動するために、docker-compose.ymlという名前のファイルを作成し、以下の内容を追加します。

version: "3"
services:
  app:
    build: .
    volumes:
      - .:/app
    ports:
      - "3000:3000"
    depends_on:
      - db
  db:
    image: mysql:5.7
    command: --default-authentication-plugin=mysql_native_password
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: root
    volumes:
      - ./docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d
volumes:
  db_data: # データを永続化するためのボリューム定義

データベースの初期化

MySQLサーバーを起動すると、docker-entrypoint-initdb.dフォルダ内のSQLファイルが自動的に実行されます。このフォルダを作成し、init.sqlという名前のファイルを作成し、以下の内容を追加します。

CREATE DATABASE IF NOT EXISTS test;
USE test;

CREATE TABLE IF NOT EXISTS items (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255),
  price FLOAT
);

Dockerfileの作成

FastifyアプリケーションをDockerイメージとしてビルドするために、プロジェクトフォルダ内にDockerfileという名前のファイルを作成し、以下の内容を追加します。

FROM node:19.7.0-alpine

WORKDIR /app

COPY package*.json ./

RUN npm install

COPY . .

EXPOSE 3000

CMD ["node", "app.js"]

アプリケーションの作成

Fastifyアプリケーションを作成します。プロジェクトディレクトリ内に新しいファイル(例: app.js)を作成し、以下のコードを追加してください:

app.js

const fastify = require("fastify")({ logger: true });
const mysql = require("mysql2");

const db = mysql.createPool({
  host: "db",
  user: "root",
  password: "root",
  database: "test",
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});

fastify.get("/", async (request, reply) => {
  reply.send({ hello: "world" });
});

fastify.post("/items", (request, reply) => {
  const { name, price } = request.body;
  db.query(
    "INSERT INTO items (name, price) VALUES (?, ?)",
    [name, price],
    (error, results) => {
      if (error) throw error;
      reply.code(201).send({ message: `Item added with ID: ${results.insertId}` });
    }
  );
});

fastify.get("/items", (request, reply) => {
  db.query("SELECT * FROM items", (error, results) => {
    if (error) throw error;
    reply.send(results);
  });
});

fastify.put("/items/:id", (request, reply) => {
  const id = parseInt(request.params.id);
  const { name, price } = request.body;
  db.query(
    "UPDATE items SET name = ?, price = ? WHERE id = ?",
    [name, price, id],
    (error, results) => {
      if (error) throw error;
      reply.send({ message: `Item with ID: ${id} has been updated.` });
    }
  );
});

fastify.delete("/items/:id", (request, reply) => {
  const id = parseInt(request.params.id);
  db.query(
    "DELETE FROM items WHERE id = ?",
    [id],
    (error, results) => {
      if (error) throw error;
      reply.send({ message: `Item with ID: ${id} has been deleted.` });
    }
  );
});

const start = async () => {
  try {
    await fastify.listen({ port: 3000 }, "0.0.0.0");
    fastify.log.info(`server listening on ${fastify.server.address().port}`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

解説

このソースコードはNode.jsでFastifyとmysql2というライブラリを使ったCRUD(Create, Read, Update, Delete)の基本的なREST APIサーバーを実装したものです。以下に詳細な解説を行います。

  • Fastifyとmysql2のインポート: 以下の行でFastifyとmysql2というライブラリをインポートしています。Fastifyは高速なWebフレームワークで、mysql2はMySQLへの接続を提供します。
const fastify = require("fastify")({ logger: true });
const mysql = require("mysql2");
  • MySQL接続の設定 mysql2を使用してMySQLデータベースへの接続を設定します。これは接続プールを作成することにより行われ、各接続の詳細(ホスト、ユーザー名、パスワード、データベース名)を指定します。
const db = mysql.createPool({
  host: "db",
  user: "root",
  password: "root",
  database: "test",
  waitForConnections: true,
  connectionLimit: 10,
  queueLimit: 0
});
  • ルートエンドポイントの作成
fastify.get("/", async (request, reply) => {
  reply.send({ hello: "world" });
});

ルートエンドポイント("/")が作成され、クライアントがこのエンドポイントへGETリクエストを送ると、“Hello world"というメッセージがレスポンスとして送られます。

  • POSTリクエストのハンドラー
fastify.post("/items", (request, reply) => {
  const { name, price } = request.body;
  db.query(
    "INSERT INTO items (name, price) VALUES (?, ?)",
    [name, price],
    (error, results) => {
      if (error) throw error;
      reply.code(201).send({ message: `Item added with ID: ${results.insertId}` });
    }
  );
});

“/items"エンドポイントに対するPOSTリクエストをハンドルする関数が設定されています。これにより、新しいアイテムをデータベースに追加できます。リクエストボディから取得したnamepriceは、SQLクエリに挿入されます。

  • GETリクエストのハンドラー
fastify.get("/items", (request, reply) => {
  db.query("SELECT * FROM items", (error, results) => {
    if (error) throw error;
    reply.send(results);
  });
});

“/items"エンドポイントに対するGETリクエストをハンドルします。これにより、データベース内のすべてのアイテムを取得できます。

  • PUTリクエストのハンドラー:
fastify.put("/items/:id", (request, reply) => {
  const id = parseInt(request.params.id);
  const { name, price } = request.body;
  db.query(
    "UPDATE items SET name = ?, price = ? WHERE id = ?",
    [name, price, id],
    (error, results) => {
      if (error) throw error;
      reply.send({ message: `Item with ID: ${id} has been updated.` });
    }
  );
});

“/items/:id"エンドポイントに対するPUTリクエストをハンドルします。これにより、特定のIDを持つアイテムを更新することができます。

  • DELETEリクエストのハンドラー:
fastify.delete("/items/:id", (request, reply) => {
  const id = parseInt(request.params.id);
  db.query(
    "DELETE FROM items WHERE id = ?",
    [id],
    (error, results) => {
      if (error) throw error;
      reply.send({ message: `Item with ID: ${id} has been deleted.` });
    }
  );
});

“/items/:id"エンドポイントに対するDELETEリクエストをハンドルします。これにより、特定のIDを持つアイテムを削除することができます。

  • サーバーの開始:
const start = async () => {
  try {
    await fastify.listen(3000, "0.0.0.0");
    fastify.log.info(`server listening on ${fastify.server.address().port}`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

最後に、サーバーが開始され、指定したポート(この場合は3000)でリスンします。エラーが発生した場合は、そのエラーがログに記録され、プロセスが終了します。

Dockerイメージのビルドと実行

以下のコマンドを使用して、Docker Composeでアプリケーションとデータベースを起動します。

docker-compose up --build

これにより、FastifyアプリケーションとMySQLサーバーがDockerコンテナ内で実行され、ホストのポート3000にマッピングされます。ブラウザやAPIテストツールなどから http://localhost:3000 にアクセスすることでFastifyアプリケーションにアクセスできます。

以上が、Dockerを使用してFastifyとMySQLの環境構築する手順です。Dockerを活用することで、環境構築の手間を減らし、開発プロセスをスムーズにすることができます。

注意: ホストのファイアウォールやネットワーク設定によっては、ポート3000へのアクセスが制限されている場合があります。必要に応じて、ファイアウォールやネットワーク設定を調整してください。

全ての作業が完了するとディレクトリ構造は以下のようになります。

fastify-docker-mysql
├── app.js
├── docker-compose.yml
├── Dockerfile
├── docker-entrypoint-initdb.d
│   └── init.sql
└── package.json

テスト

## GETリクエストを送る(ルートエンドポイント):
curl http://localhost:3000
## POSTリクエストを送る(新しいアイテムを追加):
curl -X POST -H "Content-Type: application/json" -d '{"name": "item1", "price": 100}' http://localhost:3000/items
## GETリクエストを送る(全アイテムの取得)
curl http://localhost:3000/items
## PUTリクエストを送る(特定のアイテムの更新)
curl -X PUT -H "Content-Type: application/json" -d '{"name": "updated item", "price": 150}' http://localhost:3000/items/1
## DELETEリクエストを送る(特定のアイテムの削除)
curl -X DELETE http://localhost:3000/items/1

Fastifyの初期設定ではPUTとDELETEエンドポイントは未定義のため、上記のCREATEとREADのテストだけ行います。これらのCURLコマンドを使用して、各エンドポイントの動作をテストすることができます。ただし、アプリケーションがポート3000で実行されていることを確認してください。また、必要に応じてホスト名やポート番号を変更してください。

まとめ

この記事では、Fastifyを使用してデータベースを処理する方法を紹介しました。Fastifyを使用すると、簡単にデーターベースを使用した、REST APIを作成することができます。また、Dockerを使用することで、環境構築の手間を減らし、開発プロセスをスムーズにすることができます。