koa vs express 徹底比較

koa の詳細express の詳細
AI生成コンテンツ

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

Koa vs Express — Node.js Webフレームワーク徹底比較

1. 結論

Express は豊富なミドルウェアエコシステムと圧倒的な情報量を武器に、あらゆる規模のプロジェクトで安定した選択肢です。Koaasync/await をネイティブに活かしたモダンな設計で、軽量かつ柔軟なフレームワークを求める開発者に適しています。新規プロジェクトで設計の自由度を重視するなら Koa、チーム開発や既存資産の活用を重視するなら Express を推奨します。


2. 比較表

項目KoaExpress
npm ダウンロード数(週間)約 120 万約 3,500 万
GitHub スター約 35k約 66k
最新バージョン(2024 年時点)2.x4.x(5.x beta)
バンドルサイズ(unpacked)約 60 KB約 210 KB
依存パッケージ数2431
TypeScript 対応@types/koa で対応@types/express で対応
ビルトインルーター❌ なし(別途導入)express.Router()
ビルトインボディパーサー❌ なし(別途導入)express.json()
ビルトイン静的ファイル配信❌ なしexpress.static()
非同期制御ネイティブ async/awaitコールバック中心(v5 で改善予定)
ミドルウェアモデルカスケード(玉ねぎ型)直列パイプライン
エラーハンドリングtry/catch + ctx.throw()エラーハンドリングミドルウェア
学習コストやや高い(自分で組み立てる)低い(情報・教材が豊富)
エコシステム規模中規模非常に大規模
開発元TJ Holowaychuk ら(Express と同じ)TJ Holowaychuk ら → OpenJS Foundation

3. それぞれの強み

Koa の強み

  • モダンな非同期制御: フレームワーク全体が async/await を前提に設計されており、非同期処理の記述が自然です
  • カスケード(玉ねぎ型)ミドルウェア: リクエストの「行き」と「帰り」の両方でロジックを挟めるため、ロギングやレスポンスタイム計測が直感的に書けます
  • 極めて軽量なコア: ルーターやボディパーサーすら同梱しないため、不要な機能を一切持ち込まずに済みます
  • コンテキストオブジェクト (ctx): req / res を統合した ctx により、API が整理されています
  • エラーハンドリングが簡潔: try/catch でそのまま捕捉でき、Express のような特殊な 4 引数ミドルウェアが不要です

Express の強み

  • 圧倒的なエコシステム: Passport、Multer、Helmet など、実戦で必要なミドルウェアがほぼすべて揃っています
  • 情報量の多さ: Stack Overflow、Qiita、Zenn、書籍、公式ドキュメントなど、日本語を含む学習リソースが桁違いに豊富です
  • バッテリー同梱: ルーター、ボディパーサー、静的ファイル配信がビルトインで、最小構成でもすぐに動きます
  • 採用実績: Netflix、Uber、IBM など大規模プロダクションでの実績が豊富で、チームへの説明コストが低いです
  • Express 5 への進化: 現在ベータ中の v5 では async/await 対応が強化され、Koa との差が縮まりつつあります

4. コード例で比較

4-1. 基本的な JSON API サーバー

Express

// express-app.ts
import express, { Request, Response, NextFunction } from "express";

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

// ルーティング
app.get("/api/users/:id", (req: Request, res: Response) => {
  const { id } = req.params;
  res.json({ id, name: `User ${id}`, framework: "Express" });
});

app.post("/api/users", (req: Request, res: Response) => {
  const { name } = req.body;
  res.status(201).json({ id: "new-id", name, framework: "Express" });
});

// エラーハンドリング(4 引数の特殊シグネチャ)
app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
  console.error(err.stack);
  res.status(500).json({ error: "Internal Server Error" });
});

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

Koa

// koa-app.ts
import Koa from "koa";
import Router from "@koa/router";
import bodyParser from "koa-bodyparser";

const app = new Koa();
const router = new Router();

// エラーハンドリング(カスケードの最上位に配置)
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err: any) {
    console.error(err.stack);
    ctx.status = err.status || 500;
    ctx.body = { error: "Internal Server Error" };
  }
});

app.use(bodyParser());

// ルーティング
router.get("/api/users/:id", async (ctx) => {
  const { id } = ctx.params;
  ctx.body = { id, name: `User ${id}`, framework: "Koa" };
});

router.post("/api/users", async (ctx) => {
  const { name } = ctx.request.body as { name: string };
  ctx.status = 201;
  ctx.body = { id: "new-id", name, framework: "Koa" };
});

app.use(router.routes());
app.use(router.allowedMethods());

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

ポイント: Express はルーター・ボディパーサーが組み込みですぐ書けます。Koa は @koa/routerkoa-bodyparser を別途インストールする必要がありますが、コード自体は async/await で統一されて読みやすくなっています。


4-2. カスケードミドルウェア(レスポンスタイム計測)

Koa の「玉ねぎ型」ミドルウェアが最も活きるユースケースです。

Express

// express-timing.ts
import express, { Request, Response, NextFunction } from "express";

const app = express();

// レスポンスタイム計測ミドルウェア
app.use((req: Request, res: Response, next: NextFunction) => {
  const start = Date.now();

  // レスポンス完了時にヘッダーを付与
  res.on("finish", () => {
    const ms = Date.now() - start;
    // ※ finish イベント時点ではヘッダー送信済みのため、
    // 実際にはヘッダーに書き込めない場合がある
    console.log(`${req.method} ${req.url} - ${ms}ms`);
  });

  next();
});

app.get("/", (_req: Request, res: Response) => {
  res.json({ message: "Hello Express" });
});

app.listen(3000);

Koa

// koa-timing.ts
import Koa from "koa";

const app = new Koa();

// レスポンスタイム計測ミドルウェア
app.use(async (ctx, next) => {
  const start = Date.now();
  await next(); // ← 下流のミドルウェアが完了するまで待つ
  const ms = Date.now() - start;
  ctx.set("X-Response-Time", `${ms}ms`);
  console.log(`${ctx.method} ${ctx.url} - ${ms}ms`);
});

app.use(async (ctx) => {
  ctx.body = { message: "Hello Koa" };
});

app.listen(3000);

ポイント: Koa では await next() の前後でリクエストの「行き」と「帰り」を自然に表現できます。Express では res.on("finish") のようなイベントリスナーに頼る必要があり、レスポンスヘッダーへの書き込みタイミングに注意が必要です。


4-3. 非同期エラーハンドリングの違い

Express(v4)

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

const app = express();

// ❌ Express v4 では async 関数内の例外が自動で捕捉されない
app.get("/danger", async (_req: Request, _res: Response, _next: NextFunction) => {
  // この例外は Express に伝わらず、UnhandledPromiseRejection になる
  throw new Error("Something went wrong");
});

// ✅ 安全に書くには明示的に next() へ渡す
app.get("/safe", async (req: Request, res: Response, next: NextFunction) => {
  try {
    throw new Error("Something went wrong");
  } catch (err) {
    next(err);
  }
});

app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
  res.status(500).json({ error: err.message });
});

app.listen(3000);

Koa

import Koa from "koa";

const app = new Koa();

// グローバルエラーハンドラー
app.use(async (ctx, next) => {
  try {
    await next();
  } catch (err: any) {
    ctx.status = err.status || 500;
    ctx.body = { error: err.message };
  }
});

// ✅ throw するだけで上位の try/catch に自動で伝播する
app.use(async (ctx) => {
  ctx.throw(400, "Bad Request");
});

app.listen(3000);

ポイント: Express v4 では async ハンドラ内の例外を手動で next(err) に渡す必要があり、忘れるとプロセスがクラッシュするリスクがあります。Koa では async/await の仕組みにより、例外が自然にカスケードの上位へ伝播します。


5. どちらを選ぶべきか — ユースケース別推奨

ユースケース推奨理由
チーム開発・新メンバーが多いExpress学習リソースが豊富で、経験者が多い
REST API を素早く構築したいExpressビルトイン機能だけで即座に動く
マイクロサービスの軽量 APIKoaコアが軽量で、必要な機能だけ組み込める
async/await を徹底したいKoaフレームワーク全体が async 前提の設計
複雑なミドルウェアパイプラインKoaカスケードモデルで前後処理が書きやすい
既存の Express 資産があるExpress移行コストを避けるのが合理的
Passport / Multer 等を使いたいExpress公式対応ミドルウェアがそのまま使える
NestJS / Next.js と組み合わせるExpressデフォルトアダプターとして採用されている
GraphQL サーバー(Apollo 等)どちらでも両方にアダプターが用意されている
将来性を重視状況次第Express v5 で async 対応が強化予定。Koa は安定しているが開発ペースは緩やか

6. まとめ

Express と Koa は「同じ作者が設計思想を進化させた」関係にあり、対立するものではなく世代の異なる兄弟です。

  • Express は「すぐ動く・情報が多い・エコシステムが巨大」という実務上の安心感が最大の武器です。特に Express v5 が正式リリースされれば、async/await 周りの弱点も大幅に改善される見込みです。
  • Koa は「軽量・モダン・柔軟」という設計上の美しさが魅力です。自分でアーキテクチャを組み立てたい上級者や、ミニマルな構成を好むプロジェクトに向いています。

どちらを選んでも Node.js の HTTP サーバーとしての本質は変わりません。チームのスキルセット、プロジェクトの規模、既存資産との整合性を基準に判断するのが、最も実践的な選び方です。