🟢
Node.js Expressのディレクトリ構成
今更Node.js Expressのディレクトリ構成についてメモ
※AIと壁打ちしながらまとめたので、誤っている点があるかもしれません
project-root/
├── src/ # ソースコード
│ ├── app.ts # Expressのエントリーポイント
│ ├── server.ts # サーバー起動ロジック(appを読み込む)
│ ├── config/ # 設定ファイル(DB接続, 環境変数, ロガー設定など)
│ │ └── database.ts
│ │ └── env.ts
│ ├── routes/ # ルーティング定義
│ │ └── index.ts
│ │ └── users.ts
│ ├── controllers/ # ルートに対する処理(ビジネスロジック呼び出し)
│ │ └── userController.ts
│ ├── services/ # ビジネスロジック(アプリの中核処理)
│ │ └── userService.ts
│ ├── models/ # DBモデルやスキーマ定義
│ │ └── userModel.ts
│ ├── middlewares/ # ミドルウェア(認証・バリデーション・エラーハンドラなど)
│ │ └── authMiddleware.ts
│ ├── utils/ # 共通関数やヘルパー
│ │ └── logger.ts
│ ├── validators/ # 入力バリデーション (Joi, express-validatorなど)
│ │ └── userValidator.ts
│ └── tests/ # ユニット/統合テスト
│ └── user.test.ts
│
├── .env # 環境変数(dotenvで読み込む)
├── .gitignore
├── package.json
└── README.md
処理の流れ
- クライアントからapp.tsにアクセス
- app.ts → routesのURL一覧にアクセス
- routes → controllersにてresponseを返す
※controllersでvalidationなども行う - controllers → servicesにてDBにアクセス
※modelsの型を利用
サンプルコード
prisma & MySQLを例に
server.ts
サーバーの起動
import dotenv from "dotenv";
import http from "http";
import app from "./app";
import { prisma } from "./models/userModel"; // Prisma Client
// 環境変数ロード
dotenv.config();
const PORT = process.env.PORT || 3000;
// HTTPサーバー作成
const server = http.createServer(app);
// DB接続確認 → サーバー起動
async function startServer() {
try {
await prisma.$connect();
console.log("✅ Connected to MySQL");
server.listen(PORT, () => {
console.log(`🚀 Server running on http://localhost:${PORT}`);
});
} catch (error) {
console.error("❌ Failed to connect to DB:", error);
process.exit(1);
}
}
startServer();
app.ts
大元のファイル
import express, { Application, Request, Response } from "express";
import cors from "cors";
import morgan from "morgan";
import userRoutes from "./routes/userRoutes";
const app: Application = express();
// ミドルウェア
app.use(cors());
app.use(express.json());
app.use(morgan("dev"));
// ルーティング
app.use("/api/users", userRoutes);
// ヘルスチェック
app.get("/health", (req: Request, res: Response) => {
res.json({ status: "ok" });
});
export default app;
routes
URLを書く
import { Router } from "express";
import { getUserById } from "../controllers/userController";
const router = Router();
router.get("/:id", getUserById);
export default router;
controllers
response部分を書く
import { Request, Response } from "express";
import { findUserById } from "../services/userService";
export const getUserById = async (req: Request, res: Response) => {
const user = await findUserById(Number(req.params.id));
if (!user) {
return res.status(404).json({ message: "User not found" });
}
res.json(user);
};
services
データ取得 & 整形メソッド
import { prisma } from "../models/userModel";
export const findUserById = async (id: number) => {
return await prisma.user.findUnique({
where: { id },
});
};
validators
バリデーション格納
import { z } from 'zod';
import { Request, Response, NextFunction } from 'express';
export const createUserSchema = z.object({
name: z.string().min(1, { message: '名前は必須です' }),
email: z.string().email({ message: '正しいメールアドレスを入力してください' }),
});
models
DBに合わせた型の設定など
import { PrismaClient } from "@prisma/client";
export const prisma = new PrismaClient();
export interface User {
id: string;
name: string;
email: string;
}
Discussion