最終更新日: 2023年08月15日

サーバー ディレクトリ

Nuxtはこれらのディレクトリ内のファイルを自動的にスキャンして、APIやサーバーハンドラーをHMR(Hot Module Replacement)対応で登録します。

  • ~/server/api
  • ~/server/routes
  • ~/server/middleware

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

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

例:/api/helloルートをserver/api/hello.tsファイルで作成する

  • server/api/hello.ts
  • ts
export default defineEventHandler((event) => {
  return {
    hello: "world",
  };
});

これにより、ページやコンポーネント内でこのAPIを汎用的に呼び出すことができます。

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

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

h3ユーティリティは自動的にインポートされます。

サーバー ルート

~/server/api 内のファイルは、自動的にそのルートに/apiが前置されます。

/apiの接頭辞なしでサーバールートを追加するには、~/server/routesディレクトリに配置してください。

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

上記の例の場合、http://localhost:3000/helloでアクセスできます。

サーバーミドルウェア

Nuxtは、プロジェクトのサーバーミドルウェアを作成するために、~/server/middlewareディレクトリ内のすべてのファイルを自動的に読み込みます。

ミドルウェアハンドラーは、他のサーバールートの前にすべてのリクエストで実行され、ヘッダーの追加や確認、リクエストの記録、イベントのリクエストオブジェクトの拡張などが行われます。

例:

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

サーバー プラグイン

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

  • server/plugins/nitroPlugin.ts
  • ts
export default defineNitroPlugin((nitroApp) => {
  console.log("Nitro plugin", nitroApp);
});

サーバー ユーティリティ

サーバールートは、便利なヘルパーを備えたunjs/h3によって動作します。

~/server/utilsディレクトリ内に自分でさらなるヘルパーを追加することができます。

例えば、カスタムのハンドラーユーティリティを定義することができます。このユーティリティは元のハンドラーを包み込み、最終的な応答を返す前に追加の操作を実行します。

例:

  • server/utils/handler.ts
  • ts
import type { EventHandler } from "h3";

export const defineWrappedResponseHandler = (handler: EventHandler) =>
  defineEventHandler(async (event) => {
    try {
      // ルートハンドラーの前に何かを実行してください。
      const response = await handler(event);
      // ルートハンドラーの後に何かを実行してください。
      return { response };
    } catch (err) {
      // エラーハンドリング
      return { err };
    }
  });

サーバー タイプ

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

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

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

使用例

マッチングのルートパラメタ

サーバールートでは、ファイル名内のブラケット内に動的なパラメーターを使用することができます。例えば、/api/hello/[name].tsのように、これらのパラメーターはevent.context.paramsを介してアクセスできます。

例:

  • server/api/hello/[name
  • ts
// server/api/hello/[name] ← ファイル名は括弧付き
export default defineEventHandler((event) => `Hello, ${event.context.params.name}!`);

これにより、await $fetch('/api/hello/nuxt')を使ってこのAPIを汎用的に呼び出し、「Hello, nuxt!」を取得できます。

HTTPメソッドのマッチング

ハンドルファイル名は、.get、.post、.put、.deleteなどの接尾辞を付けることで、リクエストのHTTPメソッドに一致させることができます。

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

上記の例を考えると、/testを以下のようにフェッチします:

  • GETメソッド:Testのgetハンドラーを返します。
  • POSTメソッド:Testのpostハンドラーを返します。
  • 他のどんなメソッドでも、405エラーを返します。

キャッチオール ルート

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

  • server/api/foo/[...
  • ts
export default defineEventHandler(() => `Default foo handler`);
  • server/api/[...
  • ts
export default defineEventHandler(() => `Default api handler`);

リクエストのボディ処理

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

これにより、$fetch('/api/submit', { method: 'post', body: { test: 123 } })を使ってこのAPIを汎用的に呼び出すことができます。

クエリパラメーターを使ったリクエスト処理

サンプルクエリ /api/query?param1=a&param2=b

  • server/api/query.get.ts
  • ts
export default defineEventHandler((event) => {
  const query = getQuery(event);
  return { a: query.param1, b: query.param2 };
});

エラー処理

エラーが発生しない場合、ステータスコードとして200 OKが返されます。未処理のエラーがある場合は、500 Internal Server Error HTTPエラーが返されます。

他のエラーコードを返す場合は、createErrorで例外をスローしてください。

  • server/api/validation/[id
  • ts
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
  • ts
export default defineEventHandler((event) => {
  setResponseStatus(event, 202);
});

ランタイム構成へのアクセス

  • server/api/foo.ts
  • ts
export default defineEventHandler((event) => {
  const config = useRuntimeConfig();
  return { key: config.KEY };
});

リクエストクッキーへのアクセス

  • ts
export default defineEventHandler((event) => {
  const cookies = parseCookies(event);
  return { cookies };
});

高度な使用例

Nitroの設定

nuxt.configでnitroキーを使用することで、Nitroの設定を直接設定することができます。

  • nuxt.config.ts
  • ts
export default defineNuxtConfig({
  // https://nitro.unjs.io/config
  nitro: {},
});

ネストされたルーターの使用

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

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

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

ストリームの送信(実験的)

注意: これは実験的な機能であり、Node.js環境内でのみ利用可能です。

  • server/api/foo.get.ts
  • 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
  • ts
export default defineEventHandler((event) => {
  return sendRedirect(event, '/path/redirect/to', 302)
})

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

  • server/api/legacy.ts
  • ts
export default fromNodeMiddleware((req, res) => {
  res.end("Legacy handler");
});
  • server/middleware/legacy.ts
  • ts
export default fromNodeMiddleware((req, res, next) => {
  console.log("Legacy middleware");
  next();
});

サーバーストレージ

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

例:Redisの使用

nitro.storageの使用:

  • nuxt.config.ts
  • ts
export default defineNuxtConfig({
  nitro: {
    storage: {
      'redis': {
        driver: 'redis',
        /* redis connector options */
        port: 6379, // Redis port
        host: "127.0.0.1", // Redis host
        username: "", // needs Redis >= 6
        password: "",
        db: 0, // Defaults to 0
        tls: {} // tls/ssl
      }
    }
  }
})

サーバープラグインの使用:

  • server/plugins/storage.ts
  • ts
import redisDriver from 'unstorage/drivers/redis'

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

  // Dynamically pass in credentials from runtime configuration, or other sources
  const driver = redisDriver({
      base: 'redis',
      host: useRuntimeConfig().redis.host,
      port: useRuntimeConfig().redis.port,
      /* other redis connector options */
    })

  // Mount driver
  storage.mount('redis', driver)
})
  • nuxt.config.ts
  • ts
export default defineNuxtConfig({
  runtimeConfig: {
    redis: { // Default values
      host: '',
      port: 0,
      /* other redis connector options */
    }
  }
})

server/api/test.post.ts という新しいファイルを作成してください:

  • server/api/test.post.ts
  • ts
export default defineEventHandler(async (event) => {
  const body = await readBody(event)
  await useStorage().setItem('redis:test', body)
  return 'Data is set'
})

server/api/test.get.ts という新しいファイルを作成してください:

  • server/api/test.get.ts
  • ts
export default defineEventHandler(async (event) => {
  const data = await useStorage().getItem('redis:test')
  return data
})

app.vueという新しいファイルを作成してください:

  • app.vue
  • ts
<script setup lang="ts">
  const { data: resDataSuccess } = await useFetch('/api/test', {
      method: 'post',
      body: { text: 'Nuxt is Awesome!' }
  })
  const { data: resData } = await useFetch('/api/test')
</script>

<template>
  <div>
    <div>Post state: {{ resDataSuccess }}</div>
    <div>Get Data: {{ resData.text }}</div>
  </div>
</template>