genko.meDocs

導入

ViteなどのReactアプリにgenko.meのSDKを入れ、記事一覧と記事詳細をブラウザから表示する手順。

Viteで作ったReactアプリを例に、記事一覧と記事詳細を表示するまでの手順です。React RouterやCRA系の構成でも、環境変数の読み方以外は同じです。

ワークスペースの作成とブログAPIの用意はクイックスタートの手順1〜3にまとめています。

用意するもの

  • genko.meのアカウントとワークスペース。APIの作成とAPIキーの発行にはオーナー権限が必要です。
  • 記事が1件以上、保存済みかつ公開状態で入っているAPI。下書きのままでは配信APIから取得できません。
  • React 19のアプリと、バンドラー(Viteなど)。@genko-me/reactはReact 19をpeerDependencyに指定しています。
  • ビルドに使うNode.js 20.12以降。

以下のyour-workspaceは自分のワークスペースIDに、blogは自分のエンドポイントに置き換えます。

インストール

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

@genko-me/reactは本文を描画するRichTextとシェアボタンのためのパッケージです。取得だけなら@genko-me/sdkのみで足ります。

APIキーとワークスペースIDを設定する

APIキーは管理画面の「設定・プラン」→「APIキー」(/w/your-workspace/settings/api-keys)で発行します。「選択したAPIのみ」で、ブラウザに出しても構わないAPIだけに絞ってください。この構成ではキーがバンドルに入り、誰でも読めます。

ViteではVITE_で始まる環境変数だけがimport.meta.envからブラウザ側のコードに渡ります。

.env.local
VITE_GENKO_WORKSPACE=your-workspaceVITE_GENKO_API_KEY=発行したキー

APIの定義と型を書きます。フィールドIDと種類は管理画面のスキーマに合わせてください。

src/lib/schema.ts
import { listApi } from "@genko-me/sdk";
export type Blog = { title: string; body: string };
export const apis = {  blog: listApi<Blog>({    fields: [      { id: "title", type: "text" },      { id: "body", type: "richtext" },    ],  }),};

接続するクライアントを1か所に作ります。

src/lib/genko.ts
import { createGenko } from "@genko-me/sdk";import { apis } from "./schema";
export const genko = createGenko({  workspace: import.meta.env.VITE_GENKO_WORKSPACE,  apiKey: import.meta.env.VITE_GENKO_API_KEY,  apis,});
export const client = genko.apis;

@genko-me/cliのinit / pullでschema.tsを管理画面から生成することもできます。ただし生成されるclient.tsはprocess.env.GENKO_API_KEYを読むので、Viteでは上のようにimport.meta.envへ書き換えてください。@genko-me/reactは、依存にnextがあるプロジェクトにしか追加されません。

記事一覧を出す

取得はuseEffectで行い、結果を状態に入れます。読み込み中・失敗・0件の3つを必ず用意してください。

src/components/BlogList.tsx
import { useEffect, useState } from "react";import type { GenkoEntryMeta } from "@genko-me/sdk";import { client } from "../lib/genko";import type { Blog } from "../lib/schema";
export function BlogList() {  const [posts, setPosts] = useState<(Blog & GenkoEntryMeta)[] | null>(null);  const [failed, setFailed] = useState(false);
  useEffect(() => {    let active = true;    client.blog      .list({ limit: 10, orders: "-publishedAt" })      .then((result) => { if (active) setPosts(result.contents); })      .catch(() => { if (active) setFailed(true); });    return () => { active = false; };  }, []);
  if (failed) return <p>記事を取得できませんでした。</p>;  if (!posts) return <p>読み込み中…</p>;  if (posts.length === 0) return <p>公開されている記事はありません。</p>;
  return (    <ul>      {posts.map((post) => (        <li key={post.id}>          <a href={`/blog/${post.id}`}>{post.title}</a>        </li>      ))}    </ul>  );}

list()はcontentsとtotalCountを返します。totalCountは条件に合う全件数、contents.lengthは今回取得した件数です。並び替えと絞り込みの書き方は並び替え・絞り込み・検索を参照してください。

useEffectは開発時のStrict Modeで2回実行されます。上の例のように後片付けでactiveを落とすと、古いレスポンスで状態を上書きしません。

記事詳細を出す

リッチテキストの本文はRichTextに渡します。HTML文字列をそのままJSXの波括弧に入れると、タグが文字として表示されます。

src/components/BlogPost.tsx
import { useEffect, useState } from "react";import { RichText } from "@genko-me/react";import { ShareButtons } from "@genko-me/react/share-buttons";import type { GenkoEntryMeta } from "@genko-me/sdk";import { client } from "../lib/genko";import type { Blog } from "../lib/schema";
export function BlogPost({ id }: { id: string }) {  const [post, setPost] = useState<(Blog & GenkoEntryMeta) | null | undefined>(undefined);
  useEffect(() => {    let active = true;    client.blog      .getOrNull(id)      .then((result) => { if (active) setPost(result); })      .catch(() => { if (active) setPost(null); });    return () => { active = false; };  }, [id]);
  if (post === undefined) return <p>読み込み中…</p>;  if (post === null) return <p>記事が見つかりません。</p>;
  return (    <article>      <h1>{post.title}</h1>      <RichText html={post.body} as="div" className="article-body" />      <ShareButtons url={`https://your-site.example/blog/${post.id}`} title={post.title} />    </article>  );}

getOrNull()は404のときだけnullを返します。認証エラーやサーバーエラーは例外のままなので、catchで分けて扱ってください。

本文の見出し・段落の間隔・画像の最大幅は、サイト側のCSSで指定します。

css
.article-body { line-height: 1.9; }.article-body h2 { margin-block: 2em 0.75em; font-size: 1.5em; }.article-body p { margin-block: 1em; }.article-body img { max-width: 100%; height: auto; }

ShareButtonsのurlには、配信APIのURLではなくサイト側の公開記事URLを渡します。propsとCSS変数は本文・シェアの補助部品にまとめています。

表示されないとき

状況確認すること
一覧が空記事が保存済みで「公開」か。公開終了日時を過ぎていないか
401APIキーが有効期間内か。VITE_で始まる名前にしているか。開発サーバーを再起動したか
403APIキーを発行したワークスペースと接続先が同じか
404ワークスペースIDとエンドポイントが正しいか。キーでそのAPIを許可しているか
本文がタグごと表示されるRichTextに渡さず{post.body}と書いていないか

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

最終更新: 2026年9月17日

目次