Nuxtで作る — useAsyncData で取得

入門 約10分 最終更新 2026-09-08 Nuxt 3

このガイドでは、Nuxt 3 で「お知らせ一覧」と「お知らせ詳細」の2ページを作ります。SDKは server/ 側のサーバールートからのみ呼び出し、フロントは useAsyncData でそのサーバールートを叩く構成です。APIキーはクライアントには一切渡りません。

前提:APIキーとサイトコードを取得済みで、クイックスタートのステップ2まで完了していること。

1. サーバー専用の設定(runtimeConfig)

nuxt.config.ts の runtimeConfig(トップレベル=private)にAPIキーとサイトコードを置きます。public 配下に置くとクライアントバンドルに含まれてしまうため、絶対に置きません。.env では NUXT_CMS_API_KEY / NUXT_CMS_SITE_CODE という命名で自動的に読み込まれます。

nuxt.config.ts
export default defineNuxtConfig({
  runtimeConfig: {
    cmsApiKey: '',   // env: NUXT_CMS_API_KEY
    cmsSiteCode: '', // env: NUXT_CMS_SITE_CODE
    // public には置かない=クライアントに一切出さない
  },
});

2. 一覧(server route + useAsyncData)

server/utils/cms.ts で useRuntimeConfig() からクライアントを組み立てるヘルパーを1つ作ります。

server/utils/cms.ts
import { ShareDanCmsClient } from '@sharedan/cms-sdk';

export function useCmsClient() {
  const config = useRuntimeConfig();
  return new ShareDanCmsClient({
    baseUrl: 'https://api.cms.sharedan.co.jp',
    apiKey: config.cmsApiKey,
    siteCode: config.cmsSiteCode,
  });
}

server/api/news.get.ts がそのヘルパーを使って一覧を返す薄いサーバールートです。

server/api/news.get.ts
export default defineEventHandler(async () => {
  const cms = useCmsClient();
  return cms.posts.list('news', { perPage: 20 });
});

ページ側は useAsyncData でこのサーバールートを叩くだけです。

pages/news/index.vue
<script setup lang="ts">
const { data } = await useAsyncData('news', () => $fetch('/api/news'));
</script>

<template>
  <ul>
    <li v-for="p in data?.items" :key="p.slug">
      <NuxtLink :to="`/news/${p.slug}`">{{ p.title }}</NuxtLink>
      <small>{{ p.category.name }}</small>
    </li>
  </ul>
</template>
多言語(オプション):多言語オプションを有効にしたサイトでは、サーバールート内の cms.posts.list() / cms.posts.get() に localeを渡すと言語別の公開版を取得できます。未翻訳は原文へフォールバックします。詳しくは多言語サイトを作るを参照してください。

3. 詳細ページ([slug].vue)

server/api/news/[slug].get.ts が cms.posts.get() を取得し、本文は renderProseMirror() でHTML化してからレスポンスに含めます。未公開・未存在は isNotFound を見て createError({ statusCode: 404 }) を投げます。

server/api/news/[slug].get.ts
import { renderProseMirror } from '@sharedan/cms-sdk';

export default defineEventHandler(async (event) => {
  const slug = getRouterParam(event, 'slug')!;
  const cms = useCmsClient();
  try {
    const post = await cms.posts.get('news', slug);
    return { ...post, html: renderProseMirror(post.body) };
  } catch (e: any) {
    if (e.isNotFound) {
      throw createError({ statusCode: 404, statusMessage: 'Not Found' });
    }
    throw e;
  }
});

ページ側はルートパラメータで useAsyncData し、本文は v-html で差し込みます。

pages/news/[slug].vue
<script setup lang="ts">
const route = useRoute();
const { data: post } = await useAsyncData(`news-${route.params.slug}`, () =>
  $fetch(`/api/news/${route.params.slug}`),
);
</script>

<template>
  <article>
    <h1>{{ post?.title }}</h1>
    <div v-html="post?.html" />
  </article>
</template>
composableはクライアントでも動きます。SDK呼び出しは必ず server/ 側に置き、ページのcomposableからは直接呼ばないでください。

4. routeRules でキャッシュ/プリレンダ

nuxt.config.ts の routeRules で一覧・詳細をプリレンダ(SSG)にするか、ISR相当のキャッシュにするか選べます。

nuxt.config.ts
export default defineNuxtConfig({
  routeRules: {
    '/news/**': { prerender: true }, // SSG:ビルド時に生成
    // 更新頻度が高い場合は ISR 相当のSWRキャッシュも選べる:
    // '/news/**': { swr: 3600 },
  },
});

どちらの場合も、投稿の更新反映には再ビルドまたはキャッシュのパージが必要です。管理画面からのWebhook / GitHub Actions 連携についてはWebhook / GitHub Actions でビルドを起動を参照してください。

FEEDBACK

このページはお役に立ちましたか?

改善要望・不具合・ドキュメントの分かりにくい点など、お気軽にお寄せください。いただいた内容は今後の改善に役立てます。

返信をご希望の場合のみご記入ください。

スパム対策(レートリミット・ハニーポット・送信速度チェック)はサーバ側で自動適用されます。