genko.meDocs

実践

Next.js以外のReact SPAで状態管理によるページ送りと下書きプレビューを実装し、コンテンツ更新の反映方法を選ぶ手順。

導入の一覧・詳細に、運用で必要になるものを足します。Next.js向けのPaginationやViewのような部品はないので、いずれもアプリ側のコードです。

ページ送り

limitとoffsetを状態で持ち、totalCountから最終ページを計算します。

src/components/BlogPager.tsx
import { useEffect, useState } from "react";import type { GenkoListResult } from "@genko-me/sdk";import { client } from "../lib/genko";import type { Blog } from "../lib/schema";
const PER_PAGE = 10;
export function BlogPager() {  const [page, setPage] = useState(1);  const [result, setResult] = useState<GenkoListResult<Blog> | null>(null);
  useEffect(() => {    let active = true;    client.blog      .list({ limit: PER_PAGE, offset: (page - 1) * PER_PAGE, orders: "-publishedAt" })      .then((next) => { if (active) setResult(next); })      .catch(() => { if (active) setResult(null); });    return () => { active = false; };  }, [page]);
  if (!result) return <p>読み込み中…</p>;  const lastPage = Math.max(1, Math.ceil(result.totalCount / PER_PAGE));
  return (    <nav aria-label="ページ送り">      <ol>        {result.contents.map((post) => (          <li key={post.id}><a href={`/blog/${post.id}`}>{post.title}</a></li>        ))}      </ol>      <button type="button" onClick={() => setPage((current) => current - 1)} disabled={page <= 1}>        前へ      </button>      <span aria-live="polite">{page} / {lastPage}</span>      <button type="button" onClick={() => setPage((current) => current + 1)} disabled={page >= lastPage}>        次へ      </button>    </nav>  );}

limitは1〜100です。それ以上の件数を一度に扱う場合はgetAll()が100件ずつ自動でページングします。ページ番号をURLに残したいときは、pageをルーターのクエリパラメータから読むように変えてください。aria-liveを付けておくと、切り替わった位置がスクリーンリーダーに伝わります。

保存済み下書きのプレビュー

URLのdraftKeyを読み、取得メソッドへ渡します。キーが合えば下書きが、合わなければ404になりgetOrNull()はnullを返します。

src/components/BlogPost.tsx
const draftKey = new URLSearchParams(window.location.search).get("draftKey") ?? undefined;
useEffect(() => {  let active = true;  client.blog    .getOrNull(id, draftKey ? { draftKey } : undefined)    .then((result) => { if (active) setPost(result); })    .catch(() => { if (active) setPost(null); });  return () => { active = false; };}, [id, draftKey]);

管理画面のAPI設定(/w/your-workspace/apis/blog/settings)で、プレビューURLテンプレートをSPAのルートに合わせて設定します。

text
https://your-site.example/blog/{id}?draftKey={key}

{id}と{key}は管理画面が置き換えます。詳細ページを直接開ける設定(SPAフォールバック)になっていることを確認してください。

この対象では、Next.js向けのViewが自動で行う処理は付きません。 次の3点は自分で用意します。

  • プレビュー中の表示と終了導線。 下書きを見ていることが分かる帯と、draftKeyを外して公開版に戻る操作。
  • 検索避け。 <meta name="robots" content="noindex, nofollow">の付与。SPAではサーバーからヘッダーを返せないため、プレビュー中だけdocument.headに入れる形になります。
  • 共有リンクの抑制。 プレビューURLには下書きを読めるキーが含まれます。プレビュー中はシェアボタンやcanonical URLを出さないでください。

記事の「設定」でプレビューキーを再生成すると、以前のプレビューURLは無効になります。

Webhookで再生成・再検証する

この対象では、再検証するキャッシュがありません。 SPAは表示のたびに配信APIから取得するので、記事を公開すればそのまま次の表示に反映されます。Webhookの受信処理も不要です。

Webhookが要るのは次の場合です。

  • ビルド時にコンテンツを埋め込んでいる場合(静的生成やプリレンダリング)。管理画面のWebhookの通知先をホスティング先のデプロイフックにすると、記事を保存したときに再ビルドが動きます。受信側のコードは書きません。
  • CDNやリバースプロキシでAPIの応答をキャッシュしている場合。そのキャッシュのパージAPIを叩く小さなサーバー処理を用意します。署名の検証には@genko-me/sdkのverifyGenkoSignatureが使えます(Node.jsなど、リクエストの生の本文を読める環境が必要です)。
ts
import { verifyGenkoSignature } from "@genko-me/sdk";
const rawBody = await request.text();const valid = await verifyGenkoSignature({  secret: process.env.GENKO_WEBHOOK_SECRET!,  rawBody,  signatureHeader: request.headers.get("x-genko-signature"),  timestampHeader: request.headers.get("x-genko-timestamp"),});

イベントの種類、ペイロード、配送の制限はWebhookにまとめています。

次に読む

最終更新: 2026年10月14日

目次