Nitro完全ガイド — Nuxt/H3ベースのユニバーサルサーバーエンジンでどこでも動くAPI構築
Nitroは、Nuxt 3のサーバーエンジンとして開発された、ユニバーサルなJavaScriptサーバーフレームワークです。Node.js、Cloudflare Workers、Deno、Vercel、AWS Lambdaなど、あらゆるプラットフォームで同じコードが動作し、高速なAPIとサーバーサイドロジックを構築できます。この記事では、Nitroの基本から実践的な使い方まで徹底的に解説します。
Nitroとは
Nitroは、UnJSエコシステムの一部として開発された、次世代のサーバーエンジンです。Nuxt 3のバックエンド基盤として誕生しましたが、単独でも使用できる強力なフレームワークです。
主な特徴
- ユニバーサル - あらゆるプラットフォームで動作(Node.js、Deno、Workers、Lambda等)
- 高速 - H3ベースの高性能HTTPサーバー
- 自動最適化 - プラットフォームごとに最適化されたビルド
- ファイルベースルーティング -
server/api/に配置するだけでAPIが完成 - ビルトインキャッシュ - インメモリ、Redis、Cloudflare KVなど複数のストレージ対応
- HMR対応 - 開発時の高速なホットリロード
- TypeScript完全対応 - 完全な型安全性
- ゼロコンフィグ - 設定なしで即座に利用可能
Nitroのアーキテクチャ
┌─────────────────────────────────────────┐
│ Application Code │
│ (API Routes, Middleware, Plugins) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Nitro Core │
│ (H3, unjs/*, Routing, Caching) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Platform Adapters │
│ Node / Workers / Deno / Lambda / ... │
└─────────────────────────────────────────┘
インストールとセットアップ
スタンドアロンプロジェクト
# プロジェクト作成
npm init -y
npm install nitropack
# TypeScript設定(推奨)
npm install -D typescript @types/node
# ディレクトリ構造作成
mkdir -p server/api server/middleware server/plugins
基本的なディレクトリ構造
my-nitro-app/
├── server/
│ ├── api/ # APIルート
│ │ ├── hello.ts
│ │ └── users/
│ │ ├── index.ts
│ │ └── [id].ts
│ ├── routes/ # 通常のルート
│ │ └── health.ts
│ ├── middleware/ # グローバルミドルウェア
│ │ └── auth.ts
│ └── plugins/ # サーバープラグイン
│ └── database.ts
├── nitro.config.ts # Nitro設定
├── tsconfig.json
└── package.json
最小構成の設定
// nitro.config.ts
export default defineNitroConfig({
srcDir: 'server',
});
// package.json
{
"scripts": {
"dev": "nitro dev",
"build": "nitro build",
"preview": "node .output/server/index.mjs"
}
}
API Routesの作成
基本的なAPIエンドポイント
// server/api/hello.ts
export default defineEventHandler((event) => {
return {
message: 'Hello from Nitro!',
timestamp: new Date().toISOString(),
};
});
アクセス: GET http://localhost:3000/api/hello
パラメータの取得
// server/api/users/[id].ts
export default defineEventHandler((event) => {
const id = getRouterParam(event, 'id');
return {
userId: id,
name: 'John Doe',
email: 'john@example.com',
};
});
クエリパラメータ
// server/api/search.ts
export default defineEventHandler((event) => {
const query = getQuery(event);
return {
query: query.q,
page: query.page || 1,
limit: query.limit || 10,
};
});
アクセス: GET /api/search?q=nitro&page=1&limit=20
POSTリクエストの処理
// server/api/users/index.post.ts
export default defineEventHandler(async (event) => {
const body = await readBody(event);
// バリデーション
if (!body.name || !body.email) {
throw createError({
statusCode: 400,
message: 'Name and email are required',
});
}
// データベース保存処理(仮)
const user = {
id: Date.now(),
...body,
createdAt: new Date().toISOString(),
};
return {
success: true,
user,
};
});
RESTful APIの完全実装
// server/api/posts/[id].ts
export default defineEventHandler(async (event) => {
const id = getRouterParam(event, 'id');
const method = event.node.req.method;
switch (method) {
case 'GET':
// 記事取得
return {
id,
title: 'Sample Post',
content: 'Post content...',
};
case 'PUT':
// 記事更新
const body = await readBody(event);
return {
id,
...body,
updatedAt: new Date().toISOString(),
};
case 'DELETE':
// 記事削除
return {
success: true,
id,
};
default:
throw createError({
statusCode: 405,
message: 'Method not allowed',
});
}
});
リクエスト/レスポンス処理
ヘッダーの操作
// server/api/with-headers.ts
export default defineEventHandler((event) => {
// レスポンスヘッダー設定
setResponseHeaders(event, {
'X-Custom-Header': 'value',
'Cache-Control': 'max-age=3600',
});
// 個別ヘッダー設定
setHeader(event, 'X-API-Version', '1.0');
// リクエストヘッダー取得
const userAgent = getHeader(event, 'user-agent');
const auth = getHeader(event, 'authorization');
return {
userAgent,
hasAuth: !!auth,
};
});
Cookieの操作
// server/api/auth/login.post.ts
export default defineEventHandler(async (event) => {
const { username, password } = await readBody(event);
// 認証処理(簡易版)
if (username === 'admin' && password === 'password') {
const token = 'sample-token-' + Date.now();
// Cookieを設定
setCookie(event, 'auth-token', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 60 * 60 * 24 * 7, // 7日間
});
return {
success: true,
token,
};
}
throw createError({
statusCode: 401,
message: 'Invalid credentials',
});
});
// server/api/auth/me.ts
export default defineEventHandler((event) => {
const token = getCookie(event, 'auth-token');
if (!token) {
throw createError({
statusCode: 401,
message: 'Not authenticated',
});
}
return {
username: 'admin',
token,
};
});
ステータスコードの設定
// server/api/status-examples.ts
export default defineEventHandler((event) => {
const type = getQuery(event).type;
switch (type) {
case 'created':
setResponseStatus(event, 201);
return { message: 'Resource created' };
case 'no-content':
setResponseStatus(event, 204);
return null;
case 'redirect':
return sendRedirect(event, '/api/hello', 302);
case 'error':
throw createError({
statusCode: 500,
message: 'Something went wrong',
});
default:
return { message: 'OK' };
}
});
ミドルウェア
グローバルミドルウェア
// server/middleware/logger.ts
export default defineEventHandler((event) => {
const start = Date.now();
console.log(`[${new Date().toISOString()}] ${event.node.req.method} ${event.node.req.url}`);
// レスポンス後の処理
event.node.res.on('finish', () => {
const duration = Date.now() - start;
console.log(` → ${event.node.res.statusCode} (${duration}ms)`);
});
});
CORS設定
// server/middleware/cors.ts
export default defineEventHandler((event) => {
setResponseHeaders(event, {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
});
// OPTIONSリクエスト処理
if (event.node.req.method === 'OPTIONS') {
event.node.res.statusCode = 204;
event.node.res.end();
}
});
認証ミドルウェア
// server/middleware/auth.ts
export default defineEventHandler((event) => {
// 認証が不要なパス
const publicPaths = ['/api/auth/login', '/api/health'];
if (publicPaths.some((path) => event.node.req.url?.startsWith(path))) {
return;
}
const token = getHeader(event, 'authorization')?.replace('Bearer ', '');
if (!token) {
throw createError({
statusCode: 401,
message: 'Authentication required',
});
}
// トークン検証(簡易版)
if (token !== 'valid-token') {
throw createError({
statusCode: 401,
message: 'Invalid token',
});
}
// ユーザー情報をcontextに保存
event.context.user = {
id: 'user-123',
username: 'admin',
};
});
キャッシング戦略
ビルトインキャッシュ
// server/api/cached-data.ts
export default defineEventHandler(
cachedEventHandler(
async (event) => {
// 重い処理
await new Promise((resolve) => setTimeout(resolve, 1000));
return {
data: 'Expensive data',
timestamp: Date.now(),
};
},
{
maxAge: 60 * 60, // 1時間キャッシュ
name: 'cached-data',
getKey: (event) => {
const query = getQuery(event);
return `data:${query.id}`;
},
}
)
);
ストレージの設定
// nitro.config.ts
export default defineNitroConfig({
storage: {
cache: {
driver: 'redis',
host: 'localhost',
port: 6379,
},
db: {
driver: 'fs',
base: './.data/db',
},
},
});
ストレージの利用
// server/api/storage-example.ts
export default defineEventHandler(async (event) => {
const storage = useStorage('cache');
// データ保存
await storage.setItem('user:123', {
name: 'John Doe',
email: 'john@example.com',
});
// データ取得
const user = await storage.getItem('user:123');
// データ削除
await storage.removeItem('user:123');
// キー一覧取得
const keys = await storage.getKeys('user:');
return { user, keys };
});
Cloudflare KVの使用
// nitro.config.ts
export default defineNitroConfig({
storage: {
kv: {
driver: 'cloudflare-kv-binding',
binding: 'MY_KV',
},
},
});
// server/api/kv-example.ts
export default defineEventHandler(async (event) => {
const kv = useStorage('kv');
await kv.setItem('counter', '0', {
ttl: 3600, // 1時間
});
const counter = await kv.getItem('counter');
return { counter };
});
データベース統合
Prismaの統合
// server/plugins/database.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export default defineNitroPlugin(() => {
// プラグイン初期化時に実行
console.log('Database connected');
});
// グローバルに使えるようにする
declare module 'h3' {
interface H3EventContext {
prisma: PrismaClient;
}
}
// server/utils/db.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
export const usePrisma = () => prisma;
// server/api/posts/index.ts
export default defineEventHandler(async (event) => {
const prisma = usePrisma();
const posts = await prisma.post.findMany({
include: {
author: true,
},
orderBy: {
createdAt: 'desc',
},
take: 10,
});
return { posts };
});
Drizzle ORMの使用
// server/utils/db.ts
import { drizzle } from 'drizzle-orm/better-sqlite3';
import Database from 'better-sqlite3';
const sqlite = new Database('sqlite.db');
export const db = drizzle(sqlite);
// server/api/users/index.ts
import { db } from '~/server/utils/db';
import { users } from '~/server/db/schema';
export default defineEventHandler(async (event) => {
const allUsers = await db.select().from(users);
return { users: allUsers };
});
バリデーション
Zodによるバリデーション
// server/api/users/create.post.ts
import { z } from 'zod';
const userSchema = z.object({
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).max(150).optional(),
});
export default defineEventHandler(async (event) => {
const body = await readBody(event);
// バリデーション
const result = userSchema.safeParse(body);
if (!result.success) {
throw createError({
statusCode: 400,
message: 'Validation failed',
data: result.error.issues,
});
}
// バリデーション済みデータ
const validData = result.data;
return {
success: true,
user: validData,
};
});
カスタムバリデーター
// server/utils/validate.ts
export const validateEmail = (email: string): boolean => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
};
export const validateRequired = (value: any, fieldName: string) => {
if (!value) {
throw createError({
statusCode: 400,
message: `${fieldName} is required`,
});
}
};
エラーハンドリング
グローバルエラーハンドラ
// server/middleware/error.ts
export default defineEventHandler((event) => {
// エラーハンドリング
event.node.res.on('finish', () => {
if (event.node.res.statusCode >= 400) {
console.error(`Error: ${event.node.res.statusCode} ${event.node.req.url}`);
}
});
});
カスタムエラーレスポンス
// server/api/error-example.ts
export default defineEventHandler((event) => {
throw createError({
statusCode: 404,
statusMessage: 'Not Found',
message: 'Resource not found',
data: {
code: 'RESOURCE_NOT_FOUND',
timestamp: new Date().toISOString(),
},
});
});
エラー境界の実装
// server/utils/error.ts
export class AppError extends Error {
constructor(
public statusCode: number,
message: string,
public code?: string,
public data?: any
) {
super(message);
this.name = 'AppError';
}
}
export const handleError = (error: unknown) => {
if (error instanceof AppError) {
return createError({
statusCode: error.statusCode,
message: error.message,
data: {
code: error.code,
...error.data,
},
});
}
console.error('Unexpected error:', error);
return createError({
statusCode: 500,
message: 'Internal server error',
});
};
環境変数とランタイム設定
ランタイムコンフィグ
// nitro.config.ts
export default defineNitroConfig({
runtimeConfig: {
// プライベート設定(サーバーのみ)
databaseUrl: process.env.DATABASE_URL,
apiSecret: process.env.API_SECRET,
// パブリック設定(クライアントでも利用可能)
public: {
apiBase: process.env.PUBLIC_API_BASE || '/api',
},
},
});
使用例:
// server/api/config-example.ts
export default defineEventHandler((event) => {
const config = useRuntimeConfig();
return {
apiBase: config.public.apiBase,
// databaseUrlはサーバーのみアクセス可能
hasDatabase: !!config.databaseUrl,
};
});
プラットフォーム別デプロイ
Cloudflare Workersへのデプロイ
// nitro.config.ts
export default defineNitroConfig({
preset: 'cloudflare',
});
npm run build
npx wrangler deploy
Vercelへのデプロイ
// nitro.config.ts
export default defineNitroConfig({
preset: 'vercel',
});
npm run build
vercel deploy
AWS Lambdaへのデプロイ
// nitro.config.ts
export default defineNitroConfig({
preset: 'aws-lambda',
});
Denoへのデプロイ
// nitro.config.ts
export default defineNitroConfig({
preset: 'deno-deploy',
});
Node.jsサーバー
// nitro.config.ts
export default defineNitroConfig({
preset: 'node-server',
});
npm run build
node .output/server/index.mjs
まとめ
Nitroは、モダンなサーバーアプリケーション開発のための理想的なフレームワークです。
主な利点:
- あらゆるプラットフォームで動作するユニバーサル性
- ファイルベースルーティングで直感的なAPI構築
- ビルトインキャッシュで高速化が容易
- TypeScript完全対応で型安全
- H3ベースの高性能HTTPサーバー
こんなプロジェクトに最適:
- プラットフォーム非依存のAPI開発
- Nuxt 3のバックエンド拡張
- Edge Computingを活用したい
- 高速なAPIサーバーが必要
Nitroは、「どこでも動く」という理念のもと、現代のサーバーレス・Edge Computing時代に最適化されたフレームワークです。