brand logo

ドキュメント

server

server/ ディレクトリは、アプリケーションに API とサーバーハンドラーを登録するために使用されます。

Nuxt はこれらのディレクトリ内のファイルを自動的にスキャンし、ホットモジュールリプレースメント (HMR) サポート付きで API とサーバーハンドラーを登録します。

Directory structure
-| server/
---| api/
-----| hello.ts      # /api/hello
---| routes/
-----| bonjour.ts    # /bonjour
---| middleware/
-----| log.ts        # すべてのリクエストをログ

各ファイルは defineEventHandler() または eventHandler()(エイリアス)で定義されたデフォルト関数をエクスポートする必要があります。

ハンドラーは直接 JSON データを返すか、Promise を返すか、event.node.res.end() を使用してレスポンスを送信することができます。

server/api/hello.ts
export default defineEventHandler((event) => {
  return {
    hello: 'world'
  }
})

この API は、ページやコンポーネントでユニバーサルに呼び出すことができます:

pages/index.vue
<script setup lang="ts">
const { data } = await useFetch('/api/hello')
</script>

<template>
  <pre>{{ data }}</pre>
</template>

サーバールート

~/server/api 内のファイルは、自動的にルートに /api がプレフィックスされます。

/api プレフィックスなしでサーバールートを追加するには、~/server/routes ディレクトリに配置します。

例:

server/routes/hello.ts
export default defineEventHandler(() => 'Hello World!')

上記の例では、/hello ルートは http://localhost:3000/hello でアクセス可能になります。

現在、サーバールートは pages のように動的ルートの完全な機能をサポートしていないことに注意してください。

サーバーミドルウェア

Nuxt は ~/server/middleware 内のファイルを自動的に読み込み、プロジェクトのサーバーミドルウェアを作成します。

ミドルウェアハンドラーは、ヘッダーの追加やチェック、リクエストのログ、イベントのリクエストオブジェクトの拡張を行うために、他のサーバールートの前にすべてのリクエストで実行されます。

ミドルウェアハンドラーは何も返さず(リクエストを閉じたり応答したりせず)、リクエストコンテキストを検査または拡張するか、エラーをスローするだけにするべきです。

例:

server/middleware/log.ts
export default defineEventHandler((event) => {
  console.log('New request: ' + getRequestURL(event))
})
server/middleware/auth.ts
export default defineEventHandler((event) => {
  event.context.auth = { user: 123 }
})

サーバープラグイン

Nuxt は ~/server/plugins ディレクトリ内のファイルを自動的に読み込み、Nitro プラグインとして登録します。これにより、Nitro のランタイム動作を拡張し、ライフサイクルイベントにフックすることができます。

例:

server/plugins/nitroPlugin.ts
export default defineNitroPlugin((nitroApp) => {
  console.log('Nitro plugin', nitroApp)
})
こちらも参照 Nitro Plugins

サーバーユーティリティ

サーバールートは h3js/h3 によって動作しており、便利なヘルパーセットが付属しています。

こちらも参照 利用可能な H3 リクエストヘルパー

~/server/utils ディレクトリ内に独自のヘルパーを追加することもできます。

例えば、オリジナルのハンドラーをラップし、最終的なレスポンスを返す前に追加の操作を行うカスタムハンドラーユーティリティを定義できます。

例:

server/utils/handler.ts
import type { EventHandler, EventHandlerRequest } from 'h3'

export const defineWrappedResponseHandler = <T extends EventHandlerRequest, D> (
  handler: EventHandler<T, D>
): EventHandler<T, D> =>
  defineEventHandler<T>(async event => {
    try {
      // ルートハンドラーの前に何かを行う
      const response = await handler(event)
      // ルートハンドラーの後に何かを行う
      return { response }
    } catch (err) {
      // エラーハンドリング
      return { err }
    }
  })

サーバータイプ

この機能は Nuxt >= 3.5 から利用可能です

IDE 内で 'nitro' と 'vue' からの自動インポートを明確にするために、次の内容で ~/server/tsconfig.json を追加できます:

server/tsconfig.json
{
  "extends": "../.nuxt/tsconfig.server.json"
}

現在、これらの値は型チェック時(nuxt typecheck)には尊重されませんが、IDE でより良い型ヒントを得ることができます。

レシピ

ルートパラメータ

サーバールートは、ファイル名にブラケットで囲まれた動的パラメータを使用し、event.context.params を介してアクセスできます。

server/api/hello/[name\
export default defineEventHandler((event) => {
  const name = getRouterParam(event, 'name')

  return `Hello, ${name}!`
})

代わりに、getValidatedRouterParams を Zod などのスキーマバリデーターと共に使用して、ランタイムと型の安全性を確保します。

この API は /api/hello/nuxt でユニバーサルに呼び出すことができ、Hello, nuxt! を取得します。

HTTP メソッドのマッチング

ファイル名は .get, .post, .put, .delete, ... でサフィックスを付けて、リクエストの HTTP メソッド にマッチさせることができます。

server/api/test.get.ts
export default defineEventHandler(() => 'Test get handler')
server/api/test.post.ts
export default defineEventHandler(() => 'Test post handler')

上記の例では、/test をフェッチすると:

  • GET メソッド: Test get handler を返す
  • POST メソッド: Test post handler を返す
  • その他のメソッド: 405 エラーを返す

ディレクトリ内で index.[method].ts を使用してコードを異なる構造にすることもできます。これは API 名前空間を作成するのに便利です。

export default defineEventHandler((event) => {
  // `api/foo` エンドポイントの GET リクエストを処理
})

キャッチオールルート

キャッチオールルートは、フォールバックルートの処理に役立ちます。

例えば、~/server/api/foo/[...].ts という名前のファイルを作成すると、/api/foo/bar/baz のように、どのルートハンドラーにも一致しないすべてのリクエストに対してキャッチオールルートが登録されます。

server/api/foo/[...\
export default defineEventHandler((event) => {
  // event.context.path でルートパスを取得: '/api/foo/bar/baz'
  // event.context.params._ でルートセグメントを取得: 'bar/baz'
  return `Default foo handler`
})

キャッチオールルートに名前を付けるには、~/server/api/foo/[...slug].ts を使用し、event.context.params.slug を介してアクセスします。

server/api/foo/[...slug\
export default defineEventHandler((event) => {
  // event.context.params.slug でルートセグメントを取得: 'bar/baz'
  return `Default foo handler`
})

ボディ処理

server/api/submit.post.ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  return { body }
})

代わりに、readValidatedBody を Zod などのスキーマバリデーターと共に使用して、ランタイムと型の安全性を確保します。

この API は次のようにユニバーサルに呼び出すことができます:

app.vue
async function submit() {
  const { body } = await $fetch('/api/submit', {
    method: 'post',
    body: { test: 123 }
  })
}

ファイル名で submit.post.ts を使用しているのは、リクエストボディを受け入れることができる POST メソッドのリクエストと一致させるためだけです。GET リクエスト内で readBody を使用すると、405 Method Not Allowed HTTP エラーがスローされます。

クエリパラメータ

サンプルクエリ /api/query?foo=bar&baz=qux

server/api/query.get.ts
export default defineEventHandler((event) => {
  const query = getQuery(event)

  return { a: query.foo, b: query.baz }
})

代わりに、getValidatedQuery を Zod などのスキーマバリデーターと共に使用して、ランタイムと型の安全性を確保します。

エラーハンドリング

エラーがスローされない場合、200 OK のステータスコードが返されます。

キャッチされないエラーは 500 Internal Server Error HTTP エラーを返します。

他のエラーコードを返すには、createError を使用して例外をスローします:

server/api/validation/[id\
export default defineEventHandler((event) => {
  const id = parseInt(event.context.params.id) as number

  if (!Number.isInteger(id)) {
    throw createError({
      statusCode: 400,
      statusMessage: 'ID should be an integer',
    })
  }
  return 'All good'
})

ステータスコード

他のステータスコードを返すには、setResponseStatus ユーティリティを使用します。

例えば、202 Accepted を返すには

server/api/validation/[id\
export default defineEventHandler((event) => {
  setResponseStatus(event, 202)
})

ランタイム設定

export default defineEventHandler(async (event) => {
  const config = useRuntimeConfig(event)

  const repo = await $fetch('https://api.github.com/repos/nuxt/nuxt', {
    headers: {
      Authorization: `token ${config.githubToken}`
    }
  })

  return repo
})

サーバールートのランタイムで 環境変数 によって上書きされたランタイム設定を取得するために、useRuntimeConfigevent を引数として渡すことをお勧めしますが、これはオプションです。

リクエストクッキー

server/api/cookies.ts
export default defineEventHandler((event) => {
  const cookies = parseCookies(event)

  return { cookies }
})

コンテキストとヘッダーの転送

デフォルトでは、サーバールートでフェッチリクエストを行う際に、受信リクエストのヘッダーやリクエストコンテキストは転送されません。サーバールートでフェッチリクエストを行う際に、リクエストコンテキストとヘッダーを転送するには、event.$fetch を使用します。

server/api/forward.ts
export default defineEventHandler((event) => {
  return event.$fetch('/api/forwarded')
})

転送されるべきでない ヘッダーはリクエストに含まれません。これらのヘッダーには、例えば: transfer-encoding, connection, keep-alive, upgrade, expect, host, accept

レスポンス後の Promise の待機

サーバーリクエストを処理する際、クライアントへのレスポンスをブロックしない非同期タスク(例えば、キャッシングやログ記録)を実行する必要があるかもしれません。event.waitUntil を使用して、バックグラウンドで Promise を待機し、レスポンスを遅延させずにタスクを完了させることができます。

event.waitUntil メソッドは、ハンドラーが終了する前に待機する Promise を受け入れ、レスポンスが送信された直後にサーバーがハンドラーを終了する場合でもタスクが完了することを保証します。これは、ランタイムプロバイダーと統合して、レスポンス送信後の非同期操作を処理するためのネイティブ機能を活用します。

server/api/background-task.ts
const timeConsumingBackgroundTask = async () => {
  await new Promise((resolve) => setTimeout(resolve, 1000))
};

export default eventHandler((event) => {
  // レスポンスをブロックせずにバックグラウンドタスクをスケジュール
  event.waitUntil(timeConsumingBackgroundTask())

  // クライアントに即座にレスポンスを送信
  return 'done'
});

高度な使用法

Nitro 設定

nuxt.confignitro キーを使用して、Nitro 設定 を直接設定できます。

これは高度なオプションです。カスタム設定は、Nitro が Nuxt の semver-minor バージョンでアップグレードされるときに、設定インターフェースが変更される可能性があるため、プロダクションデプロイメントに影響を与える可能性があります。

nuxt.config.ts
export default defineNuxtConfig({
  // https://nitro.build/config
  nitro: {}
})
こちらも参照 guide > concepts > server-engine

ネストされたルーター

server/api/hello/[...slug\
import { createRouter, defineEventHandler, useBase } from 'h3'

const router = createRouter()

router.get('/test', defineEventHandler(() => 'Hello World'))

export default useBase('/api/hello', router.handler)

ストリームの送信

これは実験的な機能であり、すべての環境で利用可能です。

server/api/foo.get.ts
import fs from 'node:fs'
import { sendStream } from 'h3'

export default defineEventHandler((event) => {
  return sendStream(event, fs.createReadStream('/path/to/file'))
})

リダイレクトの送信

server/api/foo.get.ts
export default defineEventHandler(async (event) => {
  await sendRedirect(event, '/path/redirect/to', 302)
})

レガシーハンドラーまたはミドルウェア

server/api/legacy.ts
export default fromNodeMiddleware((req, res) => {
  res.end('Legacy handler')
})

レガシーサポートは h3js/h3 を使用して可能ですが、できるだけレガシーハンドラーを避けることをお勧めします。

server/middleware/legacy.ts
export default fromNodeMiddleware((req, res, next) => {
  console.log('Legacy middleware')
  next()
})

next() コールバックを async または Promise を返すレガシーミドルウェアと組み合わせないでください。

サーバーストレージ

Nitro はクロスプラットフォームのストレージレイヤーを提供します。追加のストレージマウントポイントを設定するには、nitro.storage または サーバープラグイン を使用できます。

Redis ストレージを追加する例:

nitro.storage を使用:

nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    storage: {
      redis: {
        driver: 'redis',
        /* redis コネクタオプション */
        port: 6379, // Redis ポート
        host: "127.0.0.1", // Redis ホスト
        username: "", // Redis >= 6 が必要
        password: "",
        db: 0, // デフォルトは 0
        tls: {} // tls/ssl
      }
    }
  }
})

次に、API ハンドラーで:

server/api/storage/test.ts
export default defineEventHandler(async (event) => {
  // すべてのキーをリストする
  const keys = await useStorage('redis').getKeys()

  // キーを設定する
  await useStorage('redis').setItem('foo', 'bar')

  // キーを削除する
  await useStorage('redis').removeItem('foo')

  return {}
})
こちらも参照 nitro.build > guide > storage

代わりに、サーバープラグインとランタイム設定を使用してストレージマウントポイントを作成することもできます:

import redisDriver from 'unstorage/drivers/redis'

export default defineNitroPlugin(() => {
  const storage = useStorage()

  // ランタイム設定や他のソースから動的に資格情報を渡す
  const driver = redisDriver({
      base: 'redis',
      host: useRuntimeConfig().redis.host,
      port: useRuntimeConfig().redis.port,
      /* 他の redis コネクタオプション */
    })

  // ドライバーをマウント
  storage.mount('redis', driver)
})

tips

このセクションは公式ドキュメントの翻訳ではなく、本サイト独自の補足記事です。

Nuxtのserverディレクトリとは?何が嬉しいのか

Nuxt 3 では、server ディレクトリを使うことで、サーバーサイドのAPIやミドルウェア、Nitroプラグインを簡単に実装・管理できます。従来のサーバー構築に比べて、ファイルベースの自動登録やホットリロード対応などが標準で備わっているため、開発効率が大幅に向上します。

特に、APIエンドポイントを素早く作成できること、サーバーミドルウェアでリクエストの共通処理を挟めること、NitroプラグインでNitroランタイムの拡張ができることは、モダンなフルスタック開発において大きな強みです。

この補足記事では、Nuxtのserverディレクトリの機能を深掘りし、実務での活用例や注意点を丁寧に解説します。


まず結論:serverディレクトリのポイント

  • server/api 配下のファイルは自動的に /api プレフィックス付きのAPIルートになる
  • server/routes 配下は /api プレフィックスなしのサーバールートを作成可能
  • server/middleware は全リクエストに対して実行されるサーバーミドルウェアを定義できる
  • server/plugins はNitroプラグインとしてNitroのランタイムを拡張可能
  • 各ファイルは defineEventHandler() でイベントハンドラーをエクスポートし、JSONレスポンスやストリームレスポンスが可能
  • ファイルベースで自動登録されるため、設定不要で即座に利用できる
  • IDE向けに server/tsconfig.json を用意すると型補完が向上する

いつ使うべきか?使わない方がよいケースは?

使うべきケース

  • APIエンドポイントを素早く作成したいとき
  • サーバーサイドで共通処理(認証、ログ記録など)を挟みたいとき
  • Nitroのランタイムを拡張して独自の処理を追加したいとき
  • フルスタックでNuxtアプリケーション内にAPIを内包したいとき
  • SSRやISRと連携したサーバー処理を行いたいとき

使わない方がよいケース

  • 複雑なREST APIやGraphQLサーバーを別途専用サーバーで構築している場合(責務分離のため)
  • 動的ルーティングが必要なAPIで、現状 server/routes は動的ルートを完全サポートしていないため
  • 大規模なマイクロサービス構成でAPIを分散管理している場合
  • クライアント側のみで完結する処理や静的サイト生成のみの用途

実務でよくあるユースケースとサンプルコード

1. シンプルなAPIエンドポイントの作成

server/api/hello.ts に以下のように書くだけで /api/hello にアクセス可能。

export default defineEventHandler(() => {
  return { message: 'Hello from Nuxt server API!' }
})

クライアント側では useFetch('/api/hello') で簡単に呼び出せます。


2. サーバーミドルウェアでリクエストログを記録

server/middleware/log.ts にて全リクエストのURLをログ出力。

export default defineEventHandler((event) => {
  console.log(`[Request] ${event.node.req.method} ${getRequestURL(event)}`)
})

これにより、APIやページへのすべてのリクエストを一括で監視可能です。


3. NitroプラグインでNitroのライフサイクルにフック

server/plugins/nitroPlugin.ts でNitroアプリの初期化時に処理を追加。

export default defineNitroPlugin((nitroApp) => {
  console.log('Nitro app initialized:', nitroApp.options.baseURL)
})

これを使うと、Nitroの設定や外部サービス連携などを柔軟に拡張できます。


よくある落とし穴・注意点

SSRとCSRの違いによる挙動の理解

APIはサーバー側で動作するため、クライアント側で直接呼び出すときはCSR(クライアントサイドレンダリング)として動作します。SSR中にAPIを呼ぶ場合はサーバー内で完結するため、APIのレスポンスが即座に取得できます。

この違いを理解せずにAPI呼び出しを行うと、意図しないタイミングでのリクエストや二重呼び出しが発生することがあります。


Hydration(ハイドレーション)時の注意

APIレスポンスを使って初期描画を行う場合、サーバーで取得したデータとクライアントで再取得したデータが異なるとHydrationエラーが起きることがあります。APIのレスポンスはできるだけ安定した内容にするか、クライアント側での再フェッチを制御しましょう。


パフォーマンスへの影響

サーバーミドルウェアはすべてのリクエストに対して実行されるため、重い処理を入れると全体のレスポンス速度が低下します。ログや認証チェックは軽量にし、必要に応じて条件分岐で処理を限定することが重要です。


動的ルーティングの制限

現状、server/routes は動的ルートを完全サポートしていません。動的APIルートが必要な場合は、server/api 配下でパラメータを受け取る形で実装するか、別途APIサーバーを用意することを検討してください。


まとめ

Nuxtのserverディレクトリは、APIやサーバーミドルウェア、Nitroプラグインを簡単に実装できる強力な機能です。ファイルベースの自動登録やホットリロード対応により、開発効率が大幅に向上します。

実務では、シンプルなAPI作成やリクエストログ、Nitroの拡張などに活用でき、SSR/CSRの違いやパフォーマンス面の注意を理解した上で使うことが重要です。

これらを踏まえ、Nuxtのserver機能を活用してモダンなフルスタック開発をよりスムーズに進めましょう。


IDEでの型補完を強化したい場合は、server/tsconfig.json を作成し、../.nuxt/tsconfig.server.json をextendsする設定を追加すると便利です。


title: 'Nuxt サーバールートの活用ガイド:実務で役立つ使い方と注意点' description: 'Nuxt のサーバールート機能は API エンドポイントを簡単に作成できる強力な仕組みです。本記事では基本的な使い方から実務でのユースケース、よくある落とし穴まで丁寧に解説します。初〜中級者向けにわかりやすく解説。'

Nuxt サーバールートの活用ガイド

Nuxt のサーバールートは、サーバーサイドで API エンドポイントを簡単に作成できる機能です。これにより、外部サーバーを用意せずにアプリケーション内で API を実装でき、フロントエンドとバックエンドの連携がスムーズになります。

本記事では、サーバールートの基本的な使い方を補足しつつ、実務でよくあるユースケースや注意点を詳しく解説します。これから Nuxt で API を作成する方や、既存のドキュメントだけでは理解が難しいと感じている方に役立つ内容です。


まず結論:サーバールートのポイント

  • 動的ルートパラメータをファイル名のブラケットで簡単に定義できる
  • HTTP メソッドごとにファイルを分けて処理を切り替え可能
  • キャッチオールルートで柔軟なルーティングができる
  • リクエストのボディやクエリパラメータを簡単に取得・バリデーション可能
  • レスポンスステータスやエラーハンドリングも柔軟に設定できる
  • event.waitUntil でレスポンス後の非同期処理を安全に実行可能

いつ使うべきか?使わない方がよいケース

使うべきケース

  • フロントエンドと同じリポジトリ内で API を管理したいとき
  • 小〜中規模のアプリケーションで外部 API サーバーを用意したくない場合
  • SSR(サーバーサイドレンダリング)と API を密に連携させたいとき
  • 環境変数やランタイム設定を活用して安全に外部サービスと連携したい場合

使わない方がよいケース

  • 大規模なバックエンドロジックや複雑なビジネスロジックが必要な場合(専用のバックエンドを推奨)
  • 高負荷な処理や長時間実行が必要なタスク(サーバールートはレスポンスを早く返す設計が望ましい)
  • 複数のフロントエンドやサービスから共通利用される大規模 API(専用 API サーバーの方が管理しやすい)

実務でよくあるユースケースとサンプルコード

1. ユーザー名を動的に受け取る API

動的ルートパラメータを使い、URL の一部を API パラメータとして取得します。

export default defineEventHandler((event) => {
  const name = getRouterParam(event, 'name')
  return `Hello, ${name}!`
})

この例は /api/hello/nuxt のように呼び出すと "Hello, nuxt!" を返します。


2. HTTP メソッドごとに処理を分ける

GET と POST で異なる処理を実装し、RESTful な API を簡単に作成可能です。

// server/api/test.get.ts
export default defineEventHandler(() => 'GET リクエストを受け取りました')

// server/api/test.post.ts
export default defineEventHandler(() => 'POST リクエストを受け取りました')

3. POST リクエストのボディを受け取る

フォーム送信や JSON データを受け取る際に使います。

export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  return { received: body }
})

4. バックグラウンドで非同期処理を実行する

レスポンスをすぐ返しつつ、ログ記録やキャッシュ更新などの処理を遅延実行できます。

const backgroundTask = async () => {
  await new Promise(resolve => setTimeout(resolve, 1000))
}

export default defineEventHandler((event) => {
  event.waitUntil(backgroundTask())
  return '処理を開始しました'
})

よくある落とし穴・注意点

SSR と CSR の違いに注意

サーバールートはサーバー側で動作するため、クライアント側で直接アクセスできるわけではありません。API 呼び出しは $fetch などのクライアントサイドのフェッチ関数を使って行います。

Hydration 時のデータ不整合

サーバーで取得したデータとクライアントでの状態が異なると、Hydration エラーが発生することがあります。API 呼び出しはクライアント側で行い、サーバー側レンダリング時は必要最低限のデータを渡す設計が望ましいです。

パフォーマンスへの影響

API 処理が重いとレスポンスが遅くなり、ユーザー体験が悪化します。重い処理は event.waitUntil でレスポンス後に実行するか、外部ジョブキューに委譲しましょう。

バリデーションの重要性

getValidatedRouterParamsreadValidatedBody を使い、Zod などのスキーマバリデーションを導入することで、型安全かつ堅牢な API を実現できます。未検証の入力はセキュリティリスクになるため必ず検証しましょう。

不要なヘッダーの転送に注意

event.$fetch を使ってリクエストを転送する際、hostconnection などのヘッダーは自動的に除外されます。必要なヘッダーがある場合は明示的に設定してください。


まとめ

Nuxt のサーバールートは、API を簡単に実装できる便利な機能です。動的ルートや HTTP メソッドごとの分岐、ボディやクエリの取得、レスポンス制御など多彩な機能を備えています。実務ではバリデーションやパフォーマンスに注意しつつ、適切に使い分けることが重要です。

API を Nuxt プロジェクト内で完結させることで開発効率が向上し、SSR と API の連携もスムーズになります。ぜひ本記事のポイントを参考に、Nuxt サーバールートを活用してください。


API の入力バリデーションには Zod などのスキーマバリデーターを使うことを強くおすすめします。型安全でエラーを早期に検出できるため、堅牢な API を構築できます。

readBody は POST などボディを持つリクエストでのみ使用可能です。GET リクエストで使うと 405 エラーになるため注意してください。

バックグラウンド処理を実装する際は、event.waitUntil を使わないとレスポンス送信後に処理が中断される可能性があります。必ず利用しましょう。


title: 'Nitro 設定の実務的な活用と注意点:Nuxt でのサーバー機能拡張を深掘り' description: 'Nuxt の Nitro 設定を活用してサーバーサイド機能を拡張する方法を、実務でのユースケースや注意点とともに詳しく解説します。SSR/CSR の違いやパフォーマンス面の考慮も含め、初中級者向けに丁寧に説明。'

Nitro 設定の実務的な活用と注意点

Nuxt 3 では、Nitro がサーバーエンジンとして採用されており、nuxt.confignitro オプションを通じて細かいサーバー設定を行うことができます。これにより、API ルーティングの拡張やストレージの追加、ミドルウェアのカスタマイズなど、サーバーサイドの挙動を柔軟に制御可能です。

本記事では、Nitro 設定のメリットや活用シーン、実務でよくあるユースケースを具体的に紹介しつつ、SSR(サーバーサイドレンダリング)やCSR(クライアントサイドレンダリング)との関係、パフォーマンス面での注意点も解説します。Nuxt でより高度なサーバー機能を実装したい方に役立つ内容です。


まず結論:Nitro 設定のポイント

  • nuxt.confignitro キーで Nitro の挙動を直接カスタマイズできる
  • API ルーターのネストやストリーム送信、リダイレクトなど高度なサーバー処理が可能
  • クロスプラットフォーム対応のストレージ(例:Redis)を追加して状態管理やキャッシュに活用できる
  • レガシーハンドラーやミドルウェアも利用可能だが、非推奨で注意が必要
  • Nitro の設定は Nuxt のマイナーバージョンアップで変更される可能性があるため、本番環境では慎重に運用すること

いつ使うべきか・使わない方がよいケース

使うべきケース

  • Nuxt の標準 API ルーティングでは対応できない複雑なルーティングやミドルウェア処理を実装したいとき
  • ファイルストリームを直接返すなど、レスポンスの細かい制御が必要なとき
  • Redis などの外部ストレージを利用してセッション管理やキャッシュを行いたいとき
  • 既存の Node.js ミドルウェアを活用したいが、Nuxt の標準機能では難しい場合

使わない方がよいケース

  • Nitro の内部 API や設定インターフェースが頻繁に変わるため、安定性が最優先のプロダクション環境での過度なカスタマイズ
  • 単純な API エンドポイントや軽微なサーバー処理であれば、Nuxt の標準機能で十分な場合
  • レガシーハンドラーやミドルウェアは可能な限り避け、最新の Nitro API を使うことが推奨される

実務でよくあるユースケースとサンプルコード

1. ネストされた API ルーターの実装

複数の API エンドポイントをまとめて管理したい場合に便利です。例えば、/api/hello 以下に複数のルートをまとめることができます。

import { createRouter, defineEventHandler, useBase } from 'h3'

const router = createRouter()

router.get('/test', defineEventHandler(() => 'Hello World'))

export default useBase('/api/hello', router.handler)

このようにルーターをネストすることで、API の構造を整理しやすくなります。

2. ファイルストリームを返す API

大きなファイルをクライアントに送信したい場合、ストリームを使うと効率的です。

import fs from 'node:fs'
import { sendStream } from 'h3'

export default defineEventHandler((event) => {
  return sendStream(event, fs.createReadStream('/path/to/file'))
})

ストリーム送信はメモリ消費を抑えつつレスポンスを返せるため、大容量ファイルの配信に適しています。

3. Redis ストレージの追加と利用

セッション管理やキャッシュ用途で Redis を使いたい場合、Nitro のストレージレイヤーに Redis ドライバーを追加します。

// nuxt.config.ts
export default defineNuxtConfig({
  nitro: {
    storage: {
      redis: {
        driver: 'redis',
        port: 6379,
        host: "127.0.0.1",
        username: "",
        password: "",
        db: 0,
        tls: {}
      }
    }
  }
})

API ハンドラー内での利用例:

export default defineEventHandler(async (event) => {
  const storage = useStorage('redis')
  await storage.setItem('foo', 'bar')
  const value = await storage.getItem('foo')
  return { value }
})

このように簡単に外部ストレージを統合できます。


よくある落とし穴・注意点

1. SSR と CSR の違いによる影響

Nitro はサーバーサイドの処理を担うため、API のレスポンスやミドルウェアの挙動は SSR 時に影響します。CSR でのクライアント側処理とは分離して考える必要があります。特にストリーム送信やリダイレクトはサーバー側で完結するため、クライアント側での挙動を誤解しないよう注意しましょう。

2. Hydration(ハイドレーション)との関係

サーバーで生成した HTML をクライアントで再利用するハイドレーション時に、API のレスポンスやサーバー処理が不整合を起こすと表示崩れやエラーの原因になります。API の状態管理やストレージ利用は、クライアント側の状態と同期を取る設計が重要です。

3. パフォーマンスへの影響

  • レガシーハンドラーやミドルウェアは非推奨であり、next() コールバックを async 関数と組み合わせると予期せぬ挙動やパフォーマンス低下を招くことがあります。
  • ストリーム送信は効率的ですが、ファイルの読み込みやネットワーク帯域に注意が必要です。
  • Redis など外部ストレージの接続設定は適切に行い、接続エラーやタイムアウトに備えましょう。

まとめ

Nitro 設定を活用することで、Nuxt アプリケーションのサーバーサイド機能を大幅に拡張できます。API ルーティングの柔軟な管理やストリーム送信、外部ストレージの統合など、実務で役立つ機能が豊富です。ただし、設定の変更は Nuxt のマイナーバージョンアップで影響を受ける可能性があるため、本番環境では慎重に運用しましょう。

SSR と CSR の違いやハイドレーションの影響、パフォーマンス面の注意点を理解しつつ、適切な場面で Nitro 設定を活用することが、安定した Nuxt アプリケーション開発の鍵となります。


Nitro のストレージ機能は、Redis 以外にも多様なドライバーが利用可能です。用途に応じて最適なストレージを選択し、API の状態管理やキャッシュ戦略を検討しましょう。