はじめに
この記事では、Docker Compose を利用した Node.js / TypeScript / PostgreSQL の開発環境構築手順について記載します。
開発環境
開発環境は以下の通りです。
- Windows11
- Docker Engine 28.3.3
- Docker Compose 2.39.1
- PostgreSQL 17.5
- node-postgres 8.16.3
- Node.js 22.18.0
- npm 11.5.2
- @types/node 24.1.0
- ts-node 10.9.2
- TypeScript 5.9.2
Node.js / TypeScript の環境構築
まずは Node.js と TypeScript の環境を整えていきます。
package.json の作成
package.json を作成します。
コマンド実行中の質問をスキップするため、-y をつけて実行します。
npm init -y
package.json が作成されます。
{
"name": "node-postgres-crud",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}
TypeScript のインストールと設定
TypeScript 及び以下のパッケージをインストールします。
-
ts-node: TypeScript を JavaScript へコンパイルせず実行できるようにする -
@types/node: Node.js の型定義
npm install typescript ts-node @types/node --save-dev
package.json にインストールしたパッケージが表示されます。
{
...
"devDependencies": {
"@types/node": "^24.1.0",
"ts-node": "^10.9.2",
"typescript": "^5.9.2"
}
}
TypeScirpt 設定ファイルを作成します。
npx tsc --init
ソースコードの実装
HTTP サーバーを起動して、利用ポートのログを出力するコードを実装します。
import { createServer } from "http";
const server = createServer();
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
Node.js 起動スクリプトの追加
package.json に Node.js 起動スクリプトを追加します。
{
...
"scripts": {
"start": "ts-node src/index.ts",
...
},
...
}
ここまでできたら一度起動確認をします。
npm start
実装したログが出力されます。
Server is running on port 3000
Docker設定
アプリケーションコンテナとデータベースコンテナを定義します。
Dockerfile の作成
Node.js は Dockerfile からイメージをビルドしたいので、Dockerfile を作成します。
# Use the official Node.js image
FROM node:24-alpine3.20
# Create and set the working directory
WORKDIR /app
# Copy package.json and package-lock.json
COPY package*.json ./
# Install dependencies
RUN npm install
# Expose the application port
EXPOSE 3000
# Run the application
CMD ["npm", "start"]
compose.yaml の作成
以下の要件を満たす compose.yaml を作成します。
- Node.js
- コンテナ名:
app-container - ビルド:Dockerfile
- ポート:3000
- ソースコード:ホストマシンで実装
- コンテナ名:
- PostgreSQL
- コンテナ名:
db-container - ビルド:PostgreSQL 公式イメージ のタグ 17.5-alpine3.22
- ポート:5432
- 初期データを用意
- データはコンテナを削除しても残す
- ホストマシンからDB接続可能
- コンテナ名:
- コンテナ間通信可能
サービス・コンテナ名
各コンテナのサービスとコンテナ名を定義します。
services:
app:
container_name: "app-container"
db:
container_name: "db-container"
ボリューム
PostgreSQL の以下の要件を満たすため、DBコンテナ用のボリュームを定義します。
- データはコンテナを削除しても残す
services と同じ階層に定義します。
services:
...
volumes:
db-volume:
ビルド・イメージ
Appコンテナは Dockerfile から、DBコンテナは公式イメージからビルドするように定義します。
services:
app:
...
build: . # Dockerfile path
db:
...
image: postgres:17.5-alpine3.22
...
ポート
公開するポートを定義します。
services:
app:
...
ports:
- "3000:3000"
db:
...
ports:
- "5432:5432"
...
バインドマウント
AppコンテナのソースコードとDBコンテナの初期データ作成ファイル(以下)をバインドマウントします。
CREATE TABLE users(id SERIAL PRIMARY KEY, name VARCHAR(32) NOT NULL, email VARCHAR(32) NOT NULL);
INSERT INTO users (name, email) VALUES ('Wyatt', 'wyatt@example.com');
INSERT INTO users (name, email) VALUES ('Billy', 'billy@example.com');
services:
app:
...
volumes:
- type: bind
source: ./ # Source code file path
target: /app
db:
...
volumes:
- type: bind
source: ./db
target: /docker-entrypoint-initdb.d
...
初期データ作成ファイルのマウント先の値 /docker-entrypoint-initdb.d は、Docker Hub の PostgreSQL リポジトリの Initializing a fresh instance に記載があります。1
ボリュームマウント
データはコンテナを削除しても残せるようにDBボリュームをDBコンテナにマウントします。
services:
...
db:
...
volumes:
...
- type: volume
source: db-volume
target: /var/lib/postgresql/data
volumes:
db-volume:
マウント先の /var/lib/postgresql/data は、PostgreSQL サーバーがデータを保存するデフォルトのディレクトリになります。
環境変数
ホストマシンからのDBコンテナ接続やコンテナ間通信を可能にするため、DB接続情報を環境変数に定義します。
services:
app:
...
environment:
DB_HOST: db
DB_USER: user
DB_PASSWORD: userpassword
DB_NAME: sampledb
db:
...
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: userpassword
POSTGRES_DB: sampledb
完成
完成した compose.yaml は以下の通りです。
services:
app:
container_name: "app-container"
build: . # Dockerfile path
ports:
- "3000:3000"
volumes:
- type: bind
source: ./ # Source code file path
target: /app
environment:
DB_HOST: db
DB_USER: user
DB_PASSWORD: userpassword
DB_NAME: sampledb
db:
container_name: "db-container"
image: postgres:17-alpine
ports:
- "5432:5432"
volumes:
- type: bind
source: ./db
target: /docker-entrypoint-initdb.d
- type: volume
source: db-volume
target: /var/lib/postgresql/data
environment:
POSTGRES_USER: user
POSTGRES_PASSWORD: userpassword
POSTGRES_DB: sampledb
volumes:
db-volume:
動作確認
一度動作確認をします。
docker compose up --build
app-container に実装したログが出力されます。
app-container | Server is running on port 3000
コンテナ一覧を確認します。
docker container ls
compose.yaml で定義した名前通りのコンテナが作成されています。
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
02aba2b23416 node-postgres-crud-app "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 0.0.0.0:3000->3000/tcp app-container
f2b9b21c9919 postgres:17.5-alpine3.22 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 0.0.0.0:5432->5432/tcp db-container
この時点では、AppコンテナとDBコンテナがそれぞれ起動しているだけで、コンテナ間の通信は発生していません。
DB接続処理の実装
AppコンテナからDBコンテナに接続する処理を実装します。
まず PostgreSQL クライアントとして pg をインストールします。
npm install pg
npm install --save-dev @types/pg
DB接続処理を実装します。先ほどインストールした pg の Client を利用して、DBへ接続する関数を実装します。
import { Client } from "pg";
export const createConnection = async () => {
const client = new Client({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
});
try {
await client.connect();
console.log("Connected to database");
return client;
} catch (error) {
console.error("Failed to connecting to database", JSON.stringify(error));
process.exit(1);
}
};
DBへ接続する関数を src/index.ts で呼び出します。
import { createServer } from "http";
import { createConnection } from "./database";
const server = createServer();
const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
createConnection(); // Add
});
動作確認
最後に動作確認をします。
まず、コンテナを起動します。
docker compose up --build
DB接続のログも出力されます。
app-container | Connected to databas
PostgreSQL に接続します。
docker exec -it db-container psql -U user -d sampledb
初期データとして追加ユーザーが確認できます。
sampledb=# select * from users;
id | name | email
----+-------+-------------------
1 | Wyatt | wyatt@example.com
2 | Billy | billy@example.com
(2 rows)
参考
関連記事