Node.js + Express入門 - REST API開発

intermediate | 60分 で読める | 2024.12.21

公式ドキュメント

このチュートリアルで学ぶこと

  • Expressプロジェクトのセットアップ
  • ルーティングとHTTPメソッド
  • ミドルウェアの仕組み
  • リクエスト/レスポンス処理
  • エラーハンドリング
  • バリデーションとセキュリティ

前提条件

  • Node.js 18以上がインストールされていること
  • JavaScriptの基本知識

Step 1: プロジェクトセットアップ

実践メモ: cors(クロスオリジン対応)とhelmet(セキュリティヘッダー)はExpressアプリの基本的な安全対策です。最初からインストールしましょう。

mkdir express-api
cd express-api
npm init -y
npm install express cors helmet
npm install -D typescript @types/node @types/express ts-node nodemon

tsconfig.json

{
 "compilerOptions": {
 "target": "ES2020",
 "module": "commonjs",
 "strict": true,
 "esModuleInterop": true,
 "outDir": "./dist"
 },
 "include": ["src/**/*"]
}

package.json scripts

{
 "scripts": {
 "dev": "nodemon --exec ts-node src/index.ts",
 "build": "tsc",
 "start": "node dist/index.js"
 }
}

Step 2: 基本的なサーバー

// src/index.ts
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';

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

// ミドルウェア
app.use(helmet());
app.use(cors());
app.use(express.json());

// ルート
app.get('/', (req, res) => {
 res.json({ message: 'Hello, Express!' });
});

// サーバー起動
app.listen(PORT, () => {
 console.log(`Server running on http://localhost:${PORT}`);
});

Step 3: ルーティング

// src/routes/users.ts
import { Router } from 'express';

const router = Router();

interface User {
 id: number;
 name: string;
 email: string;
}

let users: User[] = [
 { id: 1, name: '田中太郎', email: 'tanaka@example.com' },
 { id: 2, name: '山田花子', email: 'yamada@example.com' },
];

// 一覧取得
router.get('/', (req, res) => {
 res.json(users);
});

// 詳細取得
router.get('/:id', (req, res) => {
 const user = users.find(u => u.id === parseInt(req.params.id));
 if (!user) {
 return res.status(404).json({ error: 'User not found' });
 }
 res.json(user);
});

// 作成
router.post('/', (req, res) => {
 const { name, email } = req.body;
 const newUser: User = {
 id: users.length + 1,
 name,
 email,
 };
 users.push(newUser);
 res.status(201).json(newUser);
});

// 更新
router.put('/:id', (req, res) => {
 const index = users.findIndex(u => u.id === parseInt(req.params.id));
 if (index === -1) {
 return res.status(404).json({ error: 'User not found' });
 }
 users[index] = { ...users[index], ...req.body };
 res.json(users[index]);
});

// 削除
router.delete('/:id', (req, res) => {
 const index = users.findIndex(u => u.id === parseInt(req.params.id));
 if (index === -1) {
 return res.status(404).json({ error: 'User not found' });
 }
 users.splice(index, 1);
 res.status(204).send();
});

export default router;

Step 4: ミドルウェア

ポイント: app.use()で登録したミドルウェアは登録順に実行されます。認証ミドルウェアはルート定義の前に配置しましょう。

// src/middleware/logger.ts
import { Request, Response, NextFunction } from 'express';

export function logger(req: Request, res: Response, next: NextFunction) {
 const start = Date.now();
 res.on('finish', () => {
 const duration = Date.now() - start;
 console.log(`${req.method} ${req.path} ${res.statusCode} - ${duration}ms`);
 });
 next();
}

// src/middleware/auth.ts
export function authenticate(req: Request, res: Response, next: NextFunction) {
 const token = req.headers.authorization?.split(' ')[1];
 if (!token) {
 return res.status(401).json({ error: 'Unauthorized' });
 }
 // トークン検証ロジック
 next();
}

Step 5: エラーハンドリング

注意: エラーハンドリングミドルウェアは引数が4つ(err, req, res, next)である必要があります。引数の数が異なると通常のミドルウェアとして扱われます。

// src/middleware/errorHandler.ts
import { Request, Response, NextFunction } from 'express';

export class AppError extends Error {
 constructor(public statusCode: number, message: string) {
 super(message);
 }
}

export function errorHandler(
 err: Error,
 req: Request,
 res: Response,
 next: NextFunction
) {
 if (err instanceof AppError) {
 return res.status(err.statusCode).json({ error: err.message });
 }
 console.error(err);
 res.status(500).json({ error: 'Internal Server Error' });
}

Step 6: アプリケーション統合

// src/index.ts
import express from 'express';
import cors from 'cors';
import helmet from 'helmet';
import userRoutes from './routes/users';
import { logger } from './middleware/logger';
import { errorHandler } from './middleware/errorHandler';

const app = express();

app.use(helmet());
app.use(cors());
app.use(express.json());
app.use(logger);

app.use('/api/users', userRoutes);

app.use(errorHandler);

app.listen(3000, () => {
 console.log('Server running on http://localhost:3000');
});

Expressで最初に整える構成

Expressは自由度が高いため、小さなAPIではすぐ動きますが、ルートが増えると構成の差が保守性に直結します。ルーティング、入力検証、ビジネスロジック、エラーハンドリングを分けると見通しが良くなります。

特にエラー処理は後回しにされがちです。非同期処理で発生した例外を共通のエラーハンドラへ流し、レスポンス形式をそろえておくと、クライアント側も扱いやすくなります。

実装時の確認

  • ルート関数に処理を詰め込みすぎていないか
  • 入力値を検証してからDB処理へ渡しているか
  • 404と500のレスポンスを分けているか
  • CORSやHelmetなど基本的なミドルウェアを検討しているか

まとめ

Expressは軽量で柔軟なNode.jsフレームワークです。ミドルウェアパターンを理解し、適切なエラーハンドリングを実装することで、堅牢なAPIを構築できます

参考リソース

← 一覧に戻る
PR
PR
PR
PR