express の使い方

Fast, unopinionated, minimalist web framework

v5.2.1/週MITWeb API
AI生成コンテンツ

この記事はAIによって生成されました。内容の正確性は保証されません。最新の情報は公式ドキュメントをご確認ください。

express の使い方 — Node.js定番Webフレームワーク完全ガイド

一言でいうと

ExpressはNode.js上で動作する、軽量かつ柔軟なWebアプリケーションフレームワークです。ルーティング・ミドルウェア・HTTPユーティリティを最小限の構成で提供し、REST APIからフルスタックWebアプリまで幅広く構築できます。

どんな時に使う?

  • REST APIサーバーの構築 — CRUD操作を提供するバックエンドAPIを素早く立ち上げたい時
  • Webアプリケーションのバックエンド — テンプレートエンジンと組み合わせてSSRするサーバーを作りたい時
  • BFF(Backend for Frontend)やプロキシサーバー — フロントエンドとマイクロサービス群の間に中間層を置きたい時

インストール

# npm
npm install express

# yarn
yarn add express

# pnpm
pnpm add express

TypeScriptで使う場合は型定義も追加します。

npm install -D @types/express typescript @types/node

注意: この記事はExpress v5.2.1 を対象としています。v5はv4から複数の破壊的変更があります。v4系を使用している場合はAPIの差異にご注意ください。

基本的な使い方(Express入門)

最もシンプルなサーバーの例です。

import express, { Request, Response } from 'express';

const app = express();
const PORT = 3000;

// JSONボディのパース(Express 4.16+ / 5.x 組み込み)
app.use(express.json());

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

// サーバー起動
app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});
# 実行(ts-node使用の場合)
npx ts-node server.ts

よく使うAPI — Express主要メソッド5選

1. app.get() / app.post() / app.put() / app.delete() — ルーティング

HTTPメソッドに対応するルートハンドラを登録します。

import express, { Request, Response } from 'express';

const app = express();
app.use(express.json());

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

const users: User[] = [
  { id: 1, name: 'Alice' },
  { id: 2, name: 'Bob' },
];

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

// 個別取得(v5ではパスパラメータの型が改善されている)
app.get('/users/:id', (req: Request, res: Response) => {
  const user = users.find((u) => u.id === Number(req.params.id));
  if (!user) {
    res.status(404).json({ error: 'User not found' });
    return;
  }
  res.json(user);
});

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

// 更新
app.put('/users/:id', (req: Request, res: Response) => {
  const user = users.find((u) => u.id === Number(req.params.id));
  if (!user) {
    res.status(404).json({ error: 'User not found' });
    return;
  }
  user.name = req.body.name;
  res.json(user);
});

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

2. app.use() — ミドルウェアの登録

リクエスト処理パイプラインに共通処理を挿入します。Expressの設計思想の中核です。

import express, { Request, Response, NextFunction } from 'express';

const app = express();

// リクエストログミドルウェア
const requestLogger = (req: Request, res: Response, next: NextFunction) => {
  const start = Date.now();
  res.on('finish', () => {
    const duration = Date.now() - start;
    console.log(`${req.method} ${req.originalUrl} ${res.statusCode} - ${duration}ms`);
  });
  next();
};

// 全ルートに適用
app.use(requestLogger);

// 特定パス配下にのみ適用
app.use('/api', express.json());

// 静的ファイル配信
app.use('/public', express.static('public'));

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

app.listen(3000);

3. express.Router() — ルーターの分割

大規模アプリケーションではルーティングをモジュール分割するのが定石です。

// routes/users.ts
import { Router, Request, Response } from 'express';

const router = Router();

router.get('/', (req: Request, res: Response) => {
  res.json({ users: [] });
});

router.get('/:id', (req: Request, res: Response) => {
  res.json({ id: req.params.id });
});

// パラメータ前処理(このRouter内の :id すべてに適用)
router.param('id', (req: Request, res: Response, next, id: string) => {
  if (!/^\d+$/.test(id)) {
    res.status(400).json({ error: 'ID must be a number' });
    return;
  }
  next();
});

export default router;
// app.ts
import express from 'express';
import usersRouter from './routes/users';

const app = express();

app.use(express.json());
app.use('/api/users', usersRouter);

app.listen(3000);

4. エラーハンドリングミドルウェア

引数が4つのミドルウェアはエラーハンドラとして認識されます。

import express, { Request, Response, NextFunction } from 'express';

const app = express();

app.get('/error-example', (req: Request, res: Response, next: NextFunction) => {
  // 同期エラーはv5では自動的にキャッチされる
  throw new Error('Something went wrong');
});

app.get('/async-error', async (req: Request, res: Response) => {
  // v5ではasyncハンドラのrejectも自動キャッチされる(v4では別途対応が必要だった)
  const data = await fetchSomethingThatMightFail();
  res.json(data);
});

// カスタムエラークラス
class AppError extends Error {
  constructor(
    public statusCode: number,
    message: string,
  ) {
    super(message);
    this.name = 'AppError';
  }
}

// エラーハンドリングミドルウェア(必ず引数4つ)
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  console.error(err.stack);

  if (err instanceof AppError) {
    res.status(err.statusCode).json({ error: err.message });
    return;
  }

  res.status(500).json({ error: 'Internal Server Error' });
});

app.listen(3000);

// ダミー関数
async function fetchSomethingThatMightFail() {
  return { ok: true };
}

v5の重要な変更: Express v5ではasyncハンドラ内でthrowされたエラーやPromiseのrejectが自動的にエラーハンドリングミドルウェアに渡されます。v4で必要だったexpress-async-errorstry-catch + next(err)パターンは不要になりました。

5. req / res の主要プロパティ・メソッド

リクエスト・レスポンスオブジェクトでよく使うものをまとめます。

import express, { Request, Response } from 'express';

const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get('/demo', (req: Request, res: Response) => {
  // --- Request ---
  console.log(req.query);        // クエリパラメータ { page: '1', limit: '10' }
  console.log(req.params);       // パスパラメータ(この例では空)
  console.log(req.body);         // リクエストボディ(POST/PUTなど)
  console.log(req.headers);      // リクエストヘッダー
  console.log(req.method);       // 'GET'
  console.log(req.path);         // '/demo'
  console.log(req.hostname);     // 'localhost'
  console.log(req.ip);           // クライアントIP
  console.log(req.get('Content-Type')); // 特定ヘッダー取得

  // --- Response ---
  res
    .status(200)                  // ステータスコード設定
    .set('X-Custom-Header', 'value') // レスポンスヘッダー設定
    .json({                       // JSONレスポンス送信
      message: 'OK',
      query: req.query,
    });

  // その他のレスポンスメソッド
  // res.send('テキスト');         // テキスト/HTML送信
  // res.sendFile('/path/to/file'); // ファイル送信
  // res.redirect('/other');       // リダイレクト
  // res.download('/path/to/file'); // ファイルダウンロード
  // res.cookie('name', 'value');  // Cookie設定(cookie-parser不要でセット可能)
  // res.clearCookie('name');      // Cookie削除
});

app.listen(3000);

類似パッケージとの比較

特徴ExpressFastifyKoaHono
設計思想ミドルウェア中心・最小構成プラグイン中心・高速async/await中心・軽量Web Standards準拠・マルチランタイム
パフォーマンス
エコシステム◎(最大)○(成長中)△(縮小傾向)○(急成長中)
TypeScriptサポート△(型定義は別パッケージ)◎(組み込み)◎(組み込み)
学習コスト低い中程度低い低い
ミドルウェア互換性Express用が膨大独自プラグイン体系Koa専用独自(Express互換アダプタあり)
対応ランタイムNode.jsNode.jsNode.jsNode.js / Deno / Bun / Cloudflare Workers

選定の目安:

  • 既存資産・情報量を重視 → Express
  • パフォーマンス最優先 → Fastify または Hono
  • エッジ環境やマルチランタイム → Hono
  • 新規プロジェクトでNode.js限定 → Fastify が有力候補

注意点・Tips

v4 → v5 の主な破壊的変更

Express v5はメジャーバージョンアップであり、以下の変更点に注意が必要です。

// ❌ v5で削除されたAPI
// app.del() → app.delete() を使用
// req.param('name') → req.params.name または req.query.name を使用
// res.json(obj, statusCode) → res.status(code).json(obj) を使用

// ✅ v5でのパスパターン変更
// v4: /users/:id? (オプショナルパラメータ)
// v5: /users{/:id} (新しい構文)
app.get('/users{/:id}', (req: Request, res: Response) => {
  // req.params.id は undefined の可能性がある
  res.json({ id: req.params.id ?? 'all' });
});

// ✅ v5ではサブアプリの req.ip がデフォルトで親アプリの設定を継承

セキュリティのベストプラクティス

import express from 'express';
import helmet from 'helmet'; // npm install helmet

const app = express();

// セキュリティヘッダーを一括設定
app.use(helmet());

// bodyサイズ制限(DoS対策)
app.use(express.json({ limit: '10kb' }));

// 本番環境ではExpressの情報を隠す
app.disable('x-powered-by'); // helmetを使えば自動で無効化される

// trust proxy設定(リバースプロキシ背後の場合)
app.set('trust proxy', 1); // ロードバランサー1段の場合

よくあるハマりポイント

// ❌ レスポンスを二重送信してしまう
app.get('/bad', (req: Request, res: Response) => {
  if (!req.query.token) {
    res.status(401).json({ error: 'Unauthorized' });
    // return を忘れると下の行も実行される!
  }
  res.json({ data: 'secret' }); // Error: Cannot set headers after they are sent

比較記事