前回の記事「DockerとExpress.jsを用いたアプリをElastic Beanstalkにデプロイする手順」では、Express.jsを使用してシンプルなWebアプリケーションを開発し、それをDockerでコンテナ化する手順を解説した。

今回は、ExpressアプリとMongoDBを接続し、データの登録から取得までを行う手順をまとめる。尚、当該手順はローカル環境での検証を前提としたものである。

環境

パッケージ

"express": "^4.19.2"
"mongoose": "^8.3.2"

Dockerバージョン: 4.26.1

Dockerの設定

docker-compose.ymlの作成

Docker Compose を使用して、Express と MongoDB のコンテナを同時に起動できるようにする。

# docker-compose記法のバージョン
version: "3.8"
# 起動するコンテナの種類を定義
services:
  # Express.jsアプリケーションレイヤーの設定
  app-name:
    build: .
    ports:
      - "3000:3000"
    environment:
      - MONGO_URI=mongodb://mongo:27017/xxxxxxDB
    depends_on:
      - mongo
    volumes:
      - .:/usr/src/app
      - /usr/src/app/node_modules
  # mongoDBのコンテナ設定
  mongo:
    image: mongo
    ports:
      - "27017:27017"
    volumes:
      - mongodata:/data/db
volumes:
  mongodata:

・サービス名(app-name)の箇所は、プロジェクト名がわかるように任意の名称を設定
・環境変数: MongoDBの接続URIを MONGO_URI 環境変数として設定。これは dist/index.js 内で使用されることを想定。

Express アプリケーションの設定

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

まず、mongooseとTypeScriptの型定義ファイルをインストールする。

$ npm i mongoose
$ npm i @types/mongoose --save-dev

TypeScript で MongoDB 接続モジュールを設定

src/db.tsを作成し、以下の実装を行う。

import mongoose from "mongoose";

const mongoURI: string =
  process.env.MONGO_URI || "mongodb://localhost:27017/xxxxxxDB";

// データベースへの接続
export const connectDB = async (): Promise<void> => {
  try {
    await mongoose.connect(mongoURI);
    console.log("MongoDB connected successfully");
  } catch (error) {
    console.error("MongoDB connection error:", error);
    // 失敗した場合はアプリケーションを終了
    process.exit(1);
  }
};

// データベースとの接続解除
export const disconnectDB = async () => {
  try {
    await mongoose.disconnect();
  } catch (error) {
    console.error("MongoDB disconnect error:", error);
    // 失敗した場合はアプリケーションを終了
    process.exit(1);
  }
};

アプリケーションでMongoDB 接続モジュールを使用する。

Expressアプリケーションのメインファイル(src/index.ts)で、この接続モジュールをインポートし、アプリケーション起動時にデータベースに接続する。

import express, { Application, Request, Response } from "express";
import { connectDB } from "./db"; // 接続モジュールをインポート

const app: Application = express();
app.use(express.json());
const port = process.env.PORT || 3000;

// DB接続
connectDB();

app.get("/", (req: Request, res: Response) => {
  res.send("Hello Docker!");
});

app.listen(port, () => {
  console.log(`App listening on port ${port}`);
});

接続確認

`npm run build` を実行し、以下のコマンドでDockerコンテナを起動する。

$ docker-compose up --build

「MongoDB connected successfully」「App listening on port 3000」のログが出力できれば接続に成功している。

ダミーデータの登録

DBとの接続に成功したので、実際にデータを登録してみる。
今回は「職種」データを表現するJobTypeモデルを作成し、
/api/master/job-typesにアクセスした際に、登録した職種の一覧が表示されるようにする。

MongoDB スキーマの設定

Mongooseを使用して、JobType スキーマを定義する。src/models/JobType.tsを作成する。

import mongoose from "mongoose";

export interface IjobType extends Document {
  name: string;
  memo?: string;
}

const jobTypeSchema = new mongoose.Schema(
  {
    name: {
      type: String,
      required: true,
    },
    memo: {
      type: String,
    },
  },
  {
    timestamps: true,
  }
);

const JobType = mongoose.model<IjobType>("JobType", jobTypeSchema);

export default JobType;

ダミーデータの挿入

src/test/models/insertData.tsを作成し、以下のコードを記述。
このファイルはnodeコマンドで個別に適用する。

import JobType from "../../models/JobType";
import { connectDB, disconnectDB } from "../../db";

// DB接続
connectDB();

async function insertDummyJobTypes() {
  const jobTypes = [
    { name: "Software Developer", memo: "Develops applications." },
    { name: "Data Scientist", memo: "Analyzes complex data." },
    { name: "Product Manager", memo: "Manages product lifecycle." },
  ];

  try {
    // 既存のデータをクリア
    await JobType.deleteMany({});
    // insert
    await JobType.insertMany(jobTypes);
    console.log("Dummy job types data inserted!");
  } catch (error) {
    console.error("Error inserting data", error);
  } finally {
    disconnectDB();
  }
}

insertDummyJobTypes();

nodeを実行して、データを登録する。

node src/test/models/insertData.ts

API エンドポイントの作成

src/routes/jobTypes.tsを作成し、JobType データの取得用のルーティングを設定する。

import express from "express";
import JobType from "../models/JobType";

const router = express.Router();

router.get("/master/job-types", async (req, res) => {
  try {
    // 一覧を取得
    const jobTypes = await JobType.find();
    res.json(jobTypes);
  } catch (error) {
    res.status(500).json({ message: "error fetch jobTypes" });
  }
});

export default router;

Expressアプリにルーターを組み込む

Expressアプリケーションのメインファイル(src/index.ts)で、ルーターの設定を行う。

import express, { Application, Request, Response } from "express";
import { connectDB } from "./db";
// ルーターをインポート
import jobTypeRouter from "./routes/jobTypes";

const app: Application = express();
const port = process.env.PORT || 3000;

// DB接続
connectDB();

app.use(express.json());
// ルーターを実行
app.use("/api", jobTypeRouter);


app.get("/", (req: Request, res: Response) => {
  res.send("Hello Docker!");
});

app.listen(port, () => {
  console.log(`App listening on port ${port}`);
});

以下のコマンドでdockerコンテナを起動する。

$ docker-compose up --build

http://localhost:3000/api/master/job-typesにアクセスすると、登録したデータが表示される。

[
{"_id":"662384c04d417182dcb2f75f","name":"Software Developer","memo":"Develops applications.","__v":0,"createdAt":"2024-04-20T09:02:56.023Z","updatedAt":"2024-04-20T09:02:56.023Z"},
{"_id":"662384c04d417182dcb2f760","name":"Data Scientist","memo":"Analyzes complex data.","__v":0,"createdAt":"2024-04-20T09:02:56.024Z","updatedAt":"2024-04-20T09:02:56.024Z"},
{"_id":"662384c04d417182dcb2f761","name":"Product Manager","memo":"Manages product lifecycle.","__v":0,"createdAt":"2024-04-20T09:02:56.024Z","updatedAt":"2024-04-20T09:02:56.024Z"}
]
カテゴリー: Node.js