genko.meDocs

導入

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は自分のエンドポイントに置き換えます。

インストール

bash
npm install @genko-me/vue @genko-me/sdk

このページは@genko-me/[email protected]に対応しています。

スキーマと型を用意する

管理画面のスキーマから型とAPI定義を生成できます。プロジェクトルートで実行します。

bash
npx @genko-me/cli init

CLIは生成先をNext.jsの構成で判断します。 Nuxt 4は既定でapp/ディレクトリを使うため、CLIはこれをNext.js App Routerのapp/と見分けられず、プロジェクトルートのlib/に出力します。app/lib/へ移し、genko.config.jsonのschemaOutput・clientOutputも書き換えてください。

genko.config.json
{  "schemaOutput": "app/lib/schema.ts",  "clientOutput": "app/lib/client.ts"}

移しておけば、以降のgenko pullもこの設定を読みますし、Nuxtの~エイリアス(srcDirであるapp/を指す)でserver/からもページからも同じ書き方で参照できます。

CLIを使わない場合は、app/lib/schema.tsを手で書きます。フィールドIDと種類は管理画面のスキーマに合わせてください。

app/lib/schema.ts
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を読みます。

app/lib/client.ts
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;
.env
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になります。

server/api/blog.get.ts
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}も自動で取得対象に入ります。

app/pages/blog/index.vue
<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を追加してください。

app/plugins/genko.ts
import { GenkoVuePlugin } from "@genko-me/vue";
export default defineNuxtPlugin(nuxtApp => { nuxtApp.vueApp.use(GenkoVuePlugin); });

ListClientの既定スロットが1件分のレイアウトです。pagination・loading・error・emptyは名前付きスロットで、繰り返しません。スロットを省略すると、その状態では何も表示しません。templateを指定すると標準メッセージを表示します。

開発サーバーを起動して/blogを開き、記事が並べば接続できています。

記事詳細を出す

server/api/blog/[id].get.ts
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>,}));
app/pages/blog/[id].vue
<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エラーはステータスが出ます
401APIキーが正しく有効期間内か。サーバープロセスでGENKO_API_KEYを読めているか
403APIキーを発行したワークスペースと接続先が同じか
404ワークスペースIDとエンドポイントblogが正しいか。キーでblogを許可しているか
~/lib/clientが見つからないCLIの生成先(lib/)と~の指す先(app/lib/)がずれています。上の「スキーマと型を用意する」の手順でファイルを移したか確認してください
下書き・ページ送りが効かないページ側のuseFetchに{ query: route.query }を渡し忘れていないか

次は実践で、ページ送りと下書きプレビューを組み立てます。

最終更新: 2026年10月16日

目次