初めてプロダクションAPIを構築した際、Express.jsを使用し、セットアップにはボイラープレート、ミドルウェア、ルートハンドラー、TypeScript設定、デプロイスクリプトを含めて約2時間かかりました。しかし最近、NitroJsを使用して完全なREST APIをわずか20分で出荷しました。`app.use()`チェーンも、手動ルーティングも、デプロイの悩みもありませんでした。
このガイドでは、NitroJsがどのようにボイラープレートを排除し、Node、Bun、Deno、サーバーレス、エッジなど、どこでも動作するプロダクション対応のバックエンドを提供するかを示します。
NitroJsとは何か、なぜバックエンドAPIに使用するのか?
NitroJsは、UnJSエコシステムが提供するサーバーツールキットで、バックエンドをビルド成果物として扱います。ランタイムに結合するサーバーを記述する代わりに、ルートハンドラーを記述し、Nitroがそれらをポータブルなバンドルにコンパイルします。Node.js、Cloudflare Workers、Vercel、またはDockerコンテナに、コードを一行も変更することなく同じコードをデプロイできます。

コアとなる考え方: サーバーではなく、ルートを書く。あなたはAPIロジックに集中し、NitroがHTTP、ミドルウェア、プラットフォームの連携を処理します。
バックエンドAPIにおける主な利点:
- ゼロ設定: `server.js`のボイラープレート不要
- ファイルシステムルーティング: `routes/users.get.ts`が`GET /users`になる
- TypeScriptネイティブ: ハンドラーからの型自動推論
- ユニバーサルデプロイメント: ワンビルド、複数ターゲット
- 組み込みキャッシュ: APIレスポンスをメモリ、Redis、またはCloudflare KVに保存
- ストレージレイヤー: 一時データのための抽象化されたファイルシステム
API開発者にとって、これはインフラストラクチャを変更する際に、より速く出荷し、リファクタリングを減らすことを意味します。
はじめる前に: NitroJsを5分でインストールする
ステップ1: プロジェクトをスキャフォールドする
# Using pnpm (recommended)
pnpm dlx giget@latest nitro nitro-api-backend
# Or npm
npm create nitro@latest nitro-api-backend
# Or bun
bunx giget@latest nitro nitro-api-backend
これにより、公式スターターテンプレートが`nitro-api-backend`にクローンされます。
ステップ2: 構造を探索する
cd nitro-api-backend
tree -L 2
出力:
├── server
│ ├── api
│ ├── routes
│ ├── tasks
│ └── middleware
├── nitro.config.ts
├── package.json
└── tsconfig.json
重要なディレクトリ:
- `server/api/`: APIルート(UIなし)
- `server/routes/`: フルスタックルート(SSR用)
- `server/middleware/`: グローバルミドルウェア
- `server/tasks/`: バックグラウンドジョブ
純粋なバックエンドAPIの場合は、`routes`を無視し、すべてを`api`に配置します。
ステップ3: 開発サーバーを実行する
pnpm install
pnpm dev
Nitroはホットリロード付きで`http://localhost:3000`で起動します。ファイルを編集すると、サーバーは200ms未満で再起動します。

最初のAPIエンドポイントを構築する
シンプルなGETハンドラー
`server/api/users.get.ts`を作成します:
// server/api/users.get.ts
export default defineEventHandler(() => {
return [
{ id: 1, name: "Alice", email: "alice@example.com" },
{ id: 2, name: "Bob", email: "bob@example.com" }
];
});
Nitroはこれを`GET /api/users`で自動的に提供します。`defineEventHandler`ラッパーは、型安全性とリクエストコンテキストへのアクセスを提供します。
パラメータを持つ動的ルート
`server/api/users/[id].get.ts`を作成します:
// server/api/users/[id].get.ts
export default defineEventHandler((event) => {
const id = getRouterParam(event, 'id');
if (!id) {
throw createError({
statusCode: 400,
statusMessage: 'User ID is required'
});
}
return {
id: Number(id),
name: `User ${id}`,
email: `user${id}@example.com`
};
});
`GET /api/users/42`にアクセスすると、`{ id: 42, name: "User 42", ... }`が表示されます。
JSONボディを持つPOST
`server/api/users.post.ts`を作成します:
// server/api/users.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event);
// Nitro parses JSON automatically
const { name, email } = body;
if (!name || !email) {
throw createError({
statusCode: 422,
statusMessage: 'Name and email are required'
});
}
// Simulate database insertion
const newUser = {
id: Math.floor(Math.random() * 1000),
name,
email,
createdAt: new Date().toISOString()
};
return newUser;
});
テストします:
curl -X POST http://localhost:3000/api/users \
-H "Content-Type: application/json" \
-d '{"name":"Charlie","email":"charlie@example.com"}'
型安全なレスポンス
Nitroは戻り値からレスポンスの型を推論します。厳密な契約のためには、明示的な型を使用します:
// server/api/products.get.ts
interface Product {
id: number;
name: string;
price: number;
inStock: boolean;
}
export default defineEventHandler<Product[]>(() => {
return [
{ id: 1, name: "Laptop", price: 1299.99, inStock: true },
{ id: 2, name: "Mouse", price: 29.99, inStock: false }
];
});
これで、このエンドポイントをインポートするクライアントは完全なIntelliSenseを利用できます。
高度なAPIパターン
認証用ミドルウェア
`server/middleware/auth.ts`を作成します:
// server/middleware/auth.ts
export default defineEventHandler(async (event) => {
const protectedRoutes = ['/api/admin', '/api/users/me'];
if (protectedRoutes.some(route => event.path.startsWith(route))) {
const token = getHeader(event, 'authorization')?.replace('Bearer ', '');
if (!token || token !== process.env.API_SECRET) {
throw createError({
statusCode: 401,
statusMessage: 'Unauthorized'
});
}
}
});
ミドルウェアはすべてのルートの前に実行されます。`nitro.config.ts`に追加します:
// nitro.config.ts
export default defineNitroConfig({
srcDir: 'server',
runtimeConfig: {
apiSecret: process.env.API_SECRET
}
});
APIレスポンスのキャッシュ
コストのかかる計算を60秒間キャッシュします:
// server/api/stats.get.ts
export default defineCachedEventHandler(async () => {
const db = useStorage('data');
const users = await db.getItem('users') || [];
return {
totalUsers: users.length,
activeUsers: users.filter(u => u.lastSeen > Date.now() - 86400000).length
};
}, {
maxAge: 60, // seconds
name: 'stats',
getKey: () => 'global-stats'
});
Nitroはキャッシュ無効化とストレージ(デプロイターゲットに応じてメモリ、Redis、またはCloudflare KV)を処理します。
ファイルアップロード処理
// server/api/upload.post.ts
export default defineEventHandler(async (event) => {
const files = await readMultipartFormData(event);
if (!files?.length) {
throw createError({ statusCode: 400, statusMessage: 'No files uploaded' });
}
const storage = useStorage('uploads');
const file = files[0];
await storage.setItem(file.filename, file.data);
return { success: true, filename: file.filename };
});
でテストします:
curl -X POST http://localhost:3000/api/upload \
-F "file=@/path/to/image.jpg"
デプロイ: ローカルからプロダクションへ
プロダクション向けビルド
pnpm build
Nitroはプラットフォーム固有のエントリポイントを持つ`.output`ディレクトリを生成します。
Node.jsで実行する
node .output/server/index.mjs
`node_modules`は不要です。ビルドがすべてをバンドルします。
Cloudflare Workersにデプロイする
# Install preset
pnpm add -D nitro-preset-cloudflare-workers
# Build with preset
NITRO_PRESET=cloudflare-workers pnpm build
# Deploy
wrangler deploy .output
Vercel (サーバーレス)にデプロイする
# Vercel detects Nitro automatically
vercel
または`nitro.config.ts`を設定します:
export default defineNitroConfig({
preset: 'vercel-edge'
});
環境変数
ローカルで`.env`を作成し、プロダクションではプラットフォームのダッシュボードを使用します:
# .env
API_SECRET=dev-secret-key
DATABASE_URL=file:./dev.db
ハンドラーでアクセスします:
const config = useRuntimeConfig();
const secret = config.apiSecret;
高度なユースケース
WebSockets & サーバー送信イベント
Nitroは`defineWebSocketHandler`を介してWebSocketsをサポートします:
// server/api/ws.ts
export default defineWebSocketHandler({
open(peer) {
console.log('WebSocket connected:', peer);
},
message(peer, message) {
peer.send(`Echo: ${message.text()}`);
},
close(peer, details) {
console.log('WebSocket closed:', details);
}
});
`ws://localhost:3000/api/ws`でアクセスします。
バックグラウンドタスク
`server/tasks/cleanup.ts`を作成します:
// server/tasks/cleanup.ts
export default defineCronHandler('0 2 * * *', async () => {
const db = useStorage('temp');
const keys = await db.getKeys();
for (const key of keys) {
const metadata = await db.getMeta(key);
if (metadata.expiresAt < Date.now()) {
await db.removeItem(key);
}
}
console.log('Cleanup completed');
});
これは毎日午前2時に実行されます。cronサポートのあるプラットフォーム(例: Vercel、Netlify)にデプロイします。
ワークフローエンジン
複雑なオーケストレーションのために、`useWorkflow.dev`と統合します:
// server/api/workflows/process.ts
import { createWorkflow } from '@useworkflow/sdk';
export default defineEventHandler(async (event) => {
const workflow = createWorkflow('data-pipeline', {
steps: [
{ id: 'fetch', run: 'https://api.example.com/data' },
{ id: 'transform', run: 'transform.js' },
{ id: 'store', run: async (data) => {
const storage = useStorage('results');
await storage.setItem('latest', data);
}}
]
});
await workflow.start();
return { status: 'started' };
});
実際の開発者シナリオ
シナリオ1: 静的サイトのバックエンド
静的なJAMstackサイトにJSONデータを提供するNitro APIをCloudflare Workersにデプロイします。無料枠で1日あたり10万リクエストを処理できます。
シナリオ2: モバイルアプリのAPI
React Nativeアプリ用のRESTエンドポイントを構築します。Nitroの組み込みCORS処理を使用します:
// nitro.config.ts
export default defineNitroConfig({
routeRules: {
'/api/**': { cors: true }
}
});
シナリオ3: マイクロサービスゲートウェイ
マイクロサービスを集約するAPIゲートウェイとしてNitroを実行します。`$fetch`を使用してリクエストをプロキシします:
// server/api/aggregate.get.ts
export default defineEventHandler(async (event) => {
const [users, posts] = await Promise.all([
$fetch('https://users-service.internal/users'),
$fetch('https://posts-service.internal/posts')
]);
return { users, posts };
});
ApidogでNitroJs APIをテストする
Nitroはバックエンドを高速で生成しますが、エンドポイントが壊れていては速度は何の意味もありません。新しい`POST /api/users`を出荷する際には、そのルート定義をApidogにインポートしてください。Apidogはハンドラーの戻り値をリバースエンジニアリングし、契約テストを自動的に生成します。

CIで実行して、フロントエンドチームが頭を抱える前に破壊的な変更を検出してください。無料で始められます。これは、迅速なNitro開発に必要なガードレールです。
よくある質問
1. NitroはExpress.jsを完全に置き換えられますか?
はい。Nitroはルーティング、ミドルウェア、デプロイメントをより良く処理します。ExpressミドルウェアをNitroプラグインに、ルートを`server/api/`に移動することで移行できます。
2. Nitroはデータベース接続をどのように処理しますか?
起動時にプールを初期化するためにNitroプラグインを使用します。ストレージレイヤーはキャッシングを抽象化しますが、SQLについては独自のORM(Prisma、Drizzleなど)を使用します。
3. Nitroはプロダクション対応ですか?
もちろんです。Nuxtは何百万ものプロダクションサイトでNitroを使用しており、規模に応じた実績があります。
4. NitroをGraphQLと一緒に使用できますか?
はい。`server/api/graphql.post.ts`にGraphQLハンドラーを追加します。任意のNode GraphQLライブラリを使用できます。Nitroはあなたを制限しません。
5. NitroとHonoの違いは何ですか?
Honoは軽量なルーターです。Nitroはビルド、プリセット、ストレージを備えた完全なサーバーツールキットです。マイクロサービスにはHonoを、フルバックエンドにはNitroを使用してください。
結論
NitroJsは、APIをポータブルな成果物にコンパイルすることで、バックエンド開発を再考します。ボイラープレートなしで、ファイルシステムルーティング、TypeScriptの安全性、ユニバーサルデプロイメントを実現します。次のREST APIをNitroで構築し、Apidogで検証してください。Nodeからエッジへの移行が1行の設定で済むとき、未来の自分に感謝することでしょう。
