導入
Nuxtに@genko-me/vueを入れ、server/ルートで取得して記事一覧と記事詳細をSSRで表示する手順。
Nuxtのアプリに接続し、記事一覧と記事詳細をサーバーで描画するまでの手順です。
ワークスペースの作成とブログAPIの用意はクイックスタートの手順1〜3にまとめています。
用意するもの
- genko.meのアカウントとワークスペース。APIの作成とAPIキーの発行にはオーナー権限が必要です。
- 記事が1件以上、保存済みかつ公開状態で入っているAPI。下書きのままでは配信APIから取得できません。
- Nuxt 4系とVue 3.5以上のプロジェクト。
@genko-me/vueはVue 3.5をpeerDependencyに指定しています。 - Node.js 20.12以降と、パッケージマネージャー。
- Nitroのプリセットは、既定の
node-serverのようにNode.js互換のランタイムを使ってください。ページ送りの署名処理がnode:cryptoを直接使うため、Cloudflare Workersなどnode:cryptoの無いランタイムでは動きません。
以下のyour-workspaceは自分のワークスペースIDに、blogは自分のエンドポイントに置き換えます。
インストール
npm install @genko-me/vue @genko-me/sdkこのページは@genko-me/[email protected]に対応しています。
スキーマと型を用意する
管理画面のスキーマから型とAPI定義を生成できます。プロジェクトルートで実行します。
npx @genko-me/cli initCLIは生成先をNext.jsの構成で判断します。 Nuxt 4は既定でapp/ディレクトリを使うため、CLIはこれをNext.js App Routerのapp/と見分けられず、プロジェクトルートのlib/に出力します。app/lib/へ移し、genko.config.jsonのschemaOutput・clientOutputも書き換えてください。
{ "schemaOutput": "app/lib/schema.ts", "clientOutput": "app/lib/client.ts"}移しておけば、以降のgenko pullもこの設定を読みますし、Nuxtの~エイリアス(srcDirであるapp/を指す)でserver/からもページからも同じ書き方で参照できます。
CLIを使わない場合は、app/lib/schema.tsを手で書きます。フィールドIDと種類は管理画面のスキーマに合わせてください。
import { listApi } from "@genko-me/sdk";
export type Blog = { title: string; body: string; publishedAt: string };
export const apis = { blog: listApi<Blog>({ fields: [ { id: "title", type: "text" }, { id: "body", type: "richtext" }, { id: "publishedAt", type: "datetime" }, ], }),};APIキーとワークスペースIDを設定する
APIキーは管理画面の「設定・プラン」→「APIキー」(/w/your-workspace/settings/api-keys)で発行します。用途がわかる名前と有効期間を設定し、「選択したAPIのみ」を選ぶ場合はblogを許可してください。
生成されたapp/lib/client.tsはそのまま使えます。process.env.GENKO_API_KEYを読みます。
import { createGenko } from "@genko-me/sdk";import { apis } from "./schema";
export const genko = createGenko({ workspace: "your-workspace", apiKey: process.env.GENKO_API_KEY!, apis,});export const client = genko.apis;GENKO_API_KEY=発行したキー環境変数名はGENKO_API_KEYのままにしてください。 ページ送りの署名・検証(@genko-me/vue/server)はこのclient.tsを経由せず、process.env.GENKO_API_KEYを直接読みます。apiKeyを別の変数から渡していても、ページ送りはこの名前を見るため、値が一致していないと設定エラーになります。
.envがGitの追跡対象になっていないことを確認してください。開発中はNuxtが自動で.envを読み込みます。本番ではホスティング先の環境変数に同じ名前で設定します。
記事一覧を出す
取得はserver/のルートで行います。fetchGenkoListの戻り値は、そのままListClientのpropになります。
import { fetchGenkoList } from "@genko-me/vue/server";import { client } from "~/lib/client";
export default defineEventHandler(event => fetchGenkoList({ api: client.blog, limit: 6, orders: "-publishedAt", href: "/blog/{id}", fields: ["title", "publishedAt"], searchParams: getQuery(event) as Record<string, string | string[] | undefined>,}));fieldsにはListItemで表示するIDを並べます。Reactは子要素を調べて必要なフィールドを判断しますが、Vueにその手段はないため、ここで明示します。省略すると絞り込まず全フィールドを取得します。スキーマにないIDを書いた場合は設定エラーになります。hrefに含めた{id}も自動で取得対象に入ります。
<script setup lang="ts">const { data } = await useFetch("/api/blog");</script>
<template> <main> <h1>ブログ</h1> <ListClient v-if="data?.ok" :initial="data.initial" :initial-page="data.initialPage" :fields="data.fields" :schemas="data.schemas" href="/blog/{id}"> <article> <h2><ListItem id="title" /></h2> <time><ListItem id="publishedAt" format="yyyy/MM/dd" time-zone="Asia/Tokyo" /></time> </article> <template #empty><Null>公開されている記事はありません。</Null></template> </ListClient> <Error v-else template /> </main></template>ListItem・Null・ErrorなどはGenkoVuePluginで登録した名前をそのまま使えます。テンプレート内で使うだけならimportは不要です。プラグインを使わない場合は、import { ListClient, ListItem, Null, Error } from "@genko-me/vue";とコンポーネントimportを追加してください。
import { GenkoVuePlugin } from "@genko-me/vue";
export default defineNuxtPlugin(nuxtApp => { nuxtApp.vueApp.use(GenkoVuePlugin); });ListClientの既定スロットが1件分のレイアウトです。pagination・loading・error・emptyは名前付きスロットで、繰り返しません。スロットを省略すると、その状態では何も表示しません。templateを指定すると標準メッセージを表示します。
開発サーバーを起動して/blogを開き、記事が並べば接続できています。
記事詳細を出す
import { fetchGenkoEntry } from "@genko-me/vue/server";import { client } from "~/lib/client";
export default defineEventHandler(event => fetchGenkoEntry({ api: client.blog, id: getRouterParam(event, "id"), fields: ["title", "body"], searchParams: getQuery(event) as Record<string, string | string[] | undefined>,}));<script setup lang="ts">const route = useRoute();const { data } = await useFetch(`/api/blog/${route.params.id}`, { query: route.query });</script>
<template> <main> <EntryProvider v-if="data?.ok && data.entry" :entry="data.entry" :fields="data.fields" :schemas="data.schemas"> <h1><ViewItem id="title" /></h1> <ViewItem id="body" /> </EntryProvider> <p v-else>記事がありません。</p> </main></template>useFetchのqueryに、ページ自身のクエリ(route.query)を必ず渡してください。 /api/blog/...は別のURLなので、/blog/xxx?draftKey=...を開いたときのdraftKeyは自動では転送されません。渡し忘れると、下書きプレビューが常に公開版と同じ結果になります(実践の下書きプレビューと、ページ送りのURLモードにも同じ注意が要ります)。
fetchGenkoEntryは404のときも例外を投げず、entry: nullで返します(data.okはtrueのまま)。取得や設定そのものに失敗した場合だけdata.okがfalseになります。
表示されないとき
| 状況 | 確認すること |
|---|---|
| 一覧が空 | 記事が保存済みで「公開」か。公開終了日時を過ぎていないか |
data.okがfalse | サーバーのログに[genko.me]で始まる行が出ています。設定エラーはその内容、HTTPエラーはステータスが出ます |
401 | APIキーが正しく有効期間内か。サーバープロセスでGENKO_API_KEYを読めているか |
403 | APIキーを発行したワークスペースと接続先が同じか |
404 | ワークスペースIDとエンドポイントblogが正しいか。キーでblogを許可しているか |
~/lib/clientが見つからない | CLIの生成先(lib/)と~の指す先(app/lib/)がずれています。上の「スキーマと型を用意する」の手順でファイルを移したか確認してください |
| 下書き・ページ送りが効かない | ページ側のuseFetchに{ query: route.query }を渡し忘れていないか |
次は実践で、ページ送りと下書きプレビューを組み立てます。
最終更新: 2026年10月16日