Nuxt3：useAsyncDataの基本的な使い方

皆さん、こんにちは。

今日は「Nuxt3：useAsyncDataの基本的な使い方」についてご紹介します。

Nuxt 3 系以降（Nuxt Bridge / Nuxt 4 などを含む）の新しいデータ取得方法として、`asyncData`や `fetch`が従来（Nuxt 2 系）のオプション・フックから、`Composable`という仕組みに変わりました。具体的には、`useAsyncData`・`useFetch`といった関数を使ってデータ取得・SSR を行います。

なお、2025年2月時点で`“Nuxt 4”`は公式の安定版としてまだ公開されていません。
一般的には、`Nuxt 3`（`Nuxt Bridge`経由で 2から3への移行などを含む）でのデータ取得方法として `useAsyncData`が広く利用されています。
Nuxt 4が正式にリリースされた際も、`useAsyncData`や`useFetch`をベースとしたデータ取得の流れはほぼ同様になると考えられます。

Nuxt 3 (あるいは Nuxt 4 で想定されるほぼ同等の書き方) をベースに、useAsyncData の使い方を解説します。

# useAsyncData の基本的な使い方

useAsyncData は サーバーサイドでのデータ取得 (SSR) とクライアントサイドでの再利用 の両方を手軽に行える Composable です。次のような流れになります。

- `useAsyncData(key, handler, options?)`の呼び出し
- 返ってきた`{ data, pending, error, refresh, ... }`オブジェクトを利用してテンプレートやスクリプトで扱う
- サーバーサイドでの初回描画時には Nuxt がデータを取得し、HTML に埋め込む
- クライアントサイドでは埋め込まれたデータを再利用し、再度の取得を不要にする（オプションにより再度フェッチさせることも可能）

## サンプルコード

```
<template>
  <div>
    <h1>ユーザー情報</h1>
    <!-- データが取得できたら表示 -->
    <div v-if="data">
      <p>ID: {{ data.id }}</p>
      <p>Name: {{ data.name }}</p>
    </div>

    <!-- 読み込み中の場合 -->
    <div v-else-if="pending">
      <p>Loading...</p>
    </div>

    <!-- エラーが発生した場合 -->
    <div v-else-if="error">
      <p style="color: red;">Error: {{ error.message }}</p>
    </div>

    <!-- 手動で再取得したい場合 -->
    <button @click="refresh">Refresh</button>
  </div>
</template>

<script setup>
// useAsyncData composable をインポート
// Nuxt 3 / 4 の場合はデフォルトで使えるため明示的なインポート不要の場合も
// import { useAsyncData } from '#app';

const { data, pending, error, refresh } = await useAsyncData(
  // 第1引数: キャッシュ用のキー (ユニークな文字列推奨)
  'fetchUser',
  // 第2引数: データ取得処理
  () => $fetch('/api/user')
  // 第3引数(オプション): 
  // { 
  //   // server: false にすると SSR 時にはリクエストしないなどの設定も可能
  //   // lazy: true にするとページ描画後に非同期でフェッチを行う
  // }
)
</script>
```

- data: 取得したデータが返る（ref でラップされている）
- pending: リクエストの進行状況（true なら通信中）
- error: 何らかのエラーが発生した場合の情報（null でないならエラー発生）
- refresh: 再度データ取得を実行するためのメソッド

# `useFetch`との違い

Nuxt 3/4 には同じようにデータ取得を行う`useFetch`という`Composable`も用意されています。

- `useFetch(url, options?)`: `fetch API`を内部的に利用
- `useAsyncData(key, handler, options?)`: `$fetch`(軽量な Fetch ラッパ) を利用

`useFetch`は内部的に ブラウザの標準`fetch`を使う点が大きな特徴で、レスポンスやエラーハンドリングなどはよりブラウザ標準に近い形で行われます。一方、`$fetch`にはデフォルトで `credentials`やヘッダー周りの扱い、エラー処理の違いなど、Nuxt 独自の利便性が加わっています。

# SSR とキャッシュ

`useAsyncData`は SSR 中に取得されたデータをクライアントサイドに埋め込み、クライアント遷移時の無駄なリクエストを省く ことができます。たとえばユーザーがページ遷移して戻ってきた際にも、キャッシュ済みのデータを再利用する設定が可能です。

```
// options パラメータの例
const { data } = await useAsyncData('fetchUser', () => $fetch('/api/user'), {
  default: () => ({ id: 0, name: '' }), // デフォルト値
  transform: (rawData) => rawData.user, // レスポンスの整形
  watch: false, // リアクティブな値の変更で再フェッチするかどうか
  immediate: true, // 初回読み込みですぐにフェッチするか
  lazy: false, // SSR 完了後、クライアントでロード時にフェッチするか
  // ...etc
})
```

# API ルートの例 (`/api/user`)

上記サンプルの`'/api/user'`は、サーバーサイドでデータを返すエンドポイントとして想定しています。Nuxt 3/4 では`server/api/`以下にファイルを置くだけで API ルートを定義でき、エンドポイントと SSR が同居 する形になります。

たとえば`server/api/user.ts`というファイルを作って以下のようにすると、`'/api/user'`でアクセスできます。

```
export default defineEventHandler(async (event) => {
  // ここでデータベースや外部APIにアクセスしたりする
  // 一旦ダミーデータを返す
  return {
    id: 1,
    name: 'Taro Nuxt'
  }
})
```

# まとめ

- `Nuxt 4`であっても`Nuxt 3`同様、`useAsyncData`や`useFetch`を使うことでサーバーサイドレンダリング + クライアントでのデータ再利用を簡単に実現できる。
- `useAsyncData`と`useFetch`の使い分けは好みによるところが大きい。`useAsyncData`で`$fetch`を使う場合のほうが`Nuxt`独自の利便性が得られる一方、標準`fetch`になじみがあれば`useFetch`でも`OK`。
- `Composable`によりコードの見通しやテストがしやすくなっているので、適切にキーやオプションを設定して `SSR + CSR`のデータ取得を最適化する。

現時点でまだ`“Nuxt 4”`は正式な情報が十分出ていない状況ですが、Nuxt 3 での useAsyncData の利用方法がそのまま応用できると考えて問題ありません。

ぜひ参考にしてみてください。

今日は以上です。

ありがとうございました。
よろしくお願いいたします。