NestJS + BFFパターンを学習するため、DevContainerで開発環境を構築しました。
学習としてBFF(Backend for Frontend)パターンを実践的に学ぶプロジェクトを立ち上げました。企画・設計はClaude Desktopとの対話で作り上げ、いざ環境構築に着手したところ、いくつかの問題に遭遇しました。
特にOracle Container Registryの認証方式が変更されていた点で解決に時間を要したため、技術記録として残しておきます。
リポジトリ: https://github.com/d-kishi/nestjs-bff-learning
企画成果物: project-plan.md
→この企画成果物をベースに、ClaudeCodeで以下のようなプロンプトで環境構築を始めました。
@docs/project-plan.md を読んで、このプロジェクトの初期セットアップを行ってください。
実施してほしいこと:
1. README.md の作成(企画書の要約版)
2. /init を実行してCLAUDE.mdを生成
3. 生成されたCLAUDE.mdを企画書の内容に基づいて調整
4. 基本的なディレクトリ構造の作成
5. ルートのpackage.json(npm workspaces設定)の作成
企画書には技術スタック、アーキテクチャ、開発規約、ADR(設計判断記録)がすべて含まれています。
技術スタック
学習プロジェクトで採用した主な技術は以下の通りです。
| カテゴリ | 技術 | 選定理由 |
|---|---|---|
| バックエンド | NestJS | TypeScript対応、モジュラー設計、学習目的に最適 |
| データベース | Oracle XE 21c | 業務で使用するDBの学習 |
| 開発環境 | DevContainer | 環境構築の再現性、VS Code統合 |
| コンテナ | Docker Desktop | DevContainerとの統合がシームレス |
Docker Desktop vs WSL + Docker Engine
最近はWSL2にDocker Engineを直接インストールし、Docker Desktopを使用しない構成も増えています。今回はDocker Desktopを採用しました。
選定理由:
- 個人学習プロジェクトのためライセンス費用は発生しない
- GUIによる管理が容易で、Docker自体の設定に時間をかけずNestJS学習に集中できる
- VS Code Dev Containers拡張との統合がシームレス
- 主目的はNestJS/BFF学習であり、Linux環境の深い理解は副次的
企業利用の場合はライセンス費用が発生する可能性があるため、WSL2 + Docker Engine構成も検討の価値があります。
アーキテクチャ概要
学習プロジェクトでは、BFF(Backend for Frontend)パターンを採用したマイクロサービス構成を目指しています。
| サービス | ポート | 役割 |
|---|---|---|
| api-gateway | 3000 | BFF層。JWT検証、サービス集約、部分失敗ハンドリング |
| task-service | 3001 | タスク管理(Project, Task, Comment, Tag) |
| user-service | 3002 | ユーザー管理・認証(User, UserProfile, Role)、JWT発行 |
| Oracle XE | 1521 | データベース |
BFF層(api-gateway)がフロントエンドからのリクエストを受け、必要に応じて複数のバックエンドサービスからデータを集約して返却します。
DevContainer環境構築
コンテナ構成
| コンテナ | 役割 | イメージ |
|---|---|---|
| app | Node.js + Oracle Instant Client | カスタムビルド |
| oracle | Oracle Database XE 21c | container-registry.oracle.com/database/express:21.3.0-xe |
devcontainer.json
{
"name": "NestJS BFF Learning",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspace",
"postCreateCommand": "npm install",
"forwardPorts": [3000, 3001, 3002, 1521],
"customizations": {
"vscode": {
"extensions": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"Orta.vscode-jest",
"ashinzekene.nestjs",
"imgildev.vscode-nestjs-snippets-extension",
"humao.rest-client",
"christian-kohler.npm-intellisense",
"christian-kohler.path-intellisense",
"yoavbls.pretty-ts-errors"
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
}
}
ポイント:
-
forwardPorts: 上記アーキテクチャ図の各サービスポート(api-gateway:3000、task-service:3001、user-service:3002)とOracle XE(1521)をホストに転送 -
postCreateCommand: コンテナ作成後に自動でnpm install
docker-compose.yml
services:
app:
build:
context: .
dockerfile: Dockerfile
volumes:
- ..:/workspace:cached
command: sleep infinity
environment:
- ORACLE_HOST=oracle
- ORACLE_PORT=1521
- ORACLE_SERVICE=XEPDB1
depends_on:
oracle:
condition: service_healthy
oracle:
image: container-registry.oracle.com/database/express:21.3.0-xe
environment:
- ORACLE_PWD=password
- ORACLE_CHARACTERSET=AL32UTF8
ports:
- "1521:1521"
volumes:
- oracle-data:/opt/oracle/oradata
- ../database/init:/opt/oracle/scripts/startup
healthcheck:
test: ["CMD-SHELL", "echo 'SELECT 1 FROM DUAL;' | sqlplus -s system/password@localhost:1521/XEPDB1 || exit 1"]
interval: 30s
timeout: 10s
retries: 10
start_period: 120s
volumes:
oracle-data:
ポイント:
-
depends_on.condition: service_healthy: Oracleのヘルスチェック完了を待機 -
start_period: 120s: Oracle初回起動に時間がかかるため余裕を持たせる -
../database/init: 初期化SQLスクリプトをマウント
Dockerfile
FROM mcr.microsoft.com/devcontainers/javascript-node:20
# Oracle Instant Client installation
# Note: libaio1 -> libaio1t64 in Debian Trixie
RUN apt-get update && apt-get install -y libaio1t64 wget unzip \
&& wget https://download.oracle.com/otn_software/linux/instantclient/2340000/instantclient-basiclite-linux.x64-23.4.0.24.05.zip \
&& unzip instantclient-basiclite-linux.x64-23.4.0.24.05.zip -d /opt/oracle \
&& rm instantclient-basiclite-linux.x64-23.4.0.24.05.zip \
&& echo /opt/oracle/instantclient_23_4 > /etc/ld.so.conf.d/oracle-instantclient.conf \
&& ldconfig \
&& apt-get clean && rm -rf /var/lib/apt/lists/*
# Set Oracle environment variables
ENV LD_LIBRARY_PATH=/opt/oracle/instantclient_23_4:$LD_LIBRARY_PATH
ENV PATH=/opt/oracle/instantclient_23_4:$PATH
# Install NestJS CLI globally
RUN npm install -g @nestjs/cli
ポイント:
-
libaio1t64: Debian Trixieでのパッケージ名(後述の「問題2」参照) - Oracle Instant Client 23.4: node-oracledbとの互換性を確保
構築時の問題と解決策
問題1: Oracle Container Registry認証エラー
現象
docker login container-registry.oracle.com
# Username: メールアドレス
# Password: Oracleアカウントのパスワード
# → Error response from daemon: unauthorized: Auth failed.
通常のOracleアカウントパスワードでは認証に失敗します。
原因
Oracle Container Registryでは、通常のパスワードではなくAuth Token(シークレットキー) が必要です。
2025年6月30日以降、Auth Token認証が必須となり、従来のOracleアカウントパスワードでの認証はできなくなりました。
解決手順
-
Oracle Container Registryにブラウザでログイン
- https://container-registry.oracle.com/ にアクセス
- 右上「Sign In」からOracleアカウントでログイン
-
Auth Tokenを生成
- 右上のプロフィール名(アカウント名) をクリック
- メニューから「Auth Token」を選択
- 「Generate Secret Key」をクリック
- 生成されたシークレットキーをコピー(この画面を閉じると再表示不可)
-
docker loginでAuth Tokenを使用
docker login container-registry.oracle.com # Username: Oracleアカウントのメールアドレス # Password: 生成したシークレットキー(Auth Token) -
イメージをpull
docker pull container-registry.oracle.com/database/express:21.3.0-xe
シークレットキーは一度しか表示されないため、安全な場所に保管してください。
また、ライセンス同意はリポジトリ詳細ページで事前に完了しておく必要があります。
問題2: Debian Trixieでlibaio1が見つからない
現象
Dockerfileのビルド中に以下のエラーが発生。
E: Unable to locate package libaio1
原因
Debian Trixie(testing)では、libaio1パッケージがlibaio1t64にリネームされています。
これはDebian/Ubuntuの64ビット移行(time_t 64-bit transition)に伴う変更です。
解決策
Dockerfileでlibaio1をlibaio1t64に変更します。
# Before
RUN apt-get install -y libaio1 ...
# After
RUN apt-get install -y libaio1t64 ...
この問題はDebian Trixie(testing)ベースのイメージで発生します。
mcr.microsoft.com/devcontainers/javascript-node:20はDebian Trixieベースです。
将来的にDebian安定版でも同様の変更が適用される可能性があります。
問題3: Oracle XE起動に時間がかかる
現象
DevContainer起動後、Oracle XEへの接続が失敗する。
原因
Oracle XEの初回起動には2-3分程度かかります。
コンテナが起動していても、データベースサービスが準備完了していません。
解決策
docker-compose.ymlでヘルスチェックを設定し、依存関係にcondition: service_healthyを指定します。
oracle:
healthcheck:
test: ["CMD-SHELL", "echo 'SELECT 1 FROM DUAL;' | sqlplus -s system/password@localhost:1521/XEPDB1 || exit 1"]
interval: 30s
timeout: 10s
retries: 10
start_period: 120s
app:
depends_on:
oracle:
condition: service_healthy
-
start_period: 120s: 初回起動の猶予期間 -
retries: 10: 最大10回リトライ - appコンテナはOracleがhealthyになるまで待機
まとめ
DevContainerでNestJS + Oracle XEの学習環境を構築しました。
特にOracle Container Registryの認証方式変更(Auth Token必須化)は、ドキュメントを見落としがちなポイントです。同様の環境を構築する方の参考になれば幸いです。