genko.meDocs

導入

SvelteKitに@genko-me/svelteを入れ、+page.server.tsで取得して記事一覧と記事詳細を表示する手順。

SvelteKitのアプリに接続し、記事一覧と記事詳細をサーバーで描画するまでの手順です。

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

用意するもの

  • genko.meのアカウントとワークスペース。APIの作成とAPIキーの発行にはオーナー権限が必要です。
  • 記事が1件以上、保存済みかつ公開状態で入っているAPI。下書きのままでは配信APIから取得できません。
  • SvelteKit 2とSvelte 5のプロジェクト。@genko-me/svelteはSvelte 5をpeerDependencyに指定しています。
  • Node.js 20.12以降と、パッケージマネージャー。
  • Node.js向けのアダプター(@sveltejs/adapter-nodeなど)。

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

インストール

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

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

スキーマと型を用意する

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

bash
npx @genko-me/cli init

CLIは生成先をNext.jsの構成で判断するため、SvelteKitではプロジェクトルートのlib/に出力します。 src/lib/へ移し、genko.config.jsonのschemaOutput・clientOutputも書き換えてください。次回以降のgenko pullはこの設定を読みます。

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

CLIを使わない場合は、src/lib/schema.tsを手で書きます。フィールド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" },    ],  }),};

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

APIキーは管理画面の「設定・プラン」→「APIキー」(/w/your-workspace/settings/api-keys)で発行します。用途がわかる名前と有効期間を設定し、「選択したAPIのみ」を選ぶ場合はblogを許可してください。

生成されたsrc/lib/client.tsのprocess.env.GENKO_API_KEYを、SvelteKitの$env/dynamic/privateに置き換えます。

src/lib/client.ts
import { createGenko } from "@genko-me/sdk";import { env } from "$env/dynamic/private";import { apis } from "./schema";
if (!env.GENKO_API_KEY) throw new Error("環境変数GENKO_API_KEYを設定してください。");
export const genko = createGenko({  workspace: "your-workspace",  apiKey: env.GENKO_API_KEY,  apis,});export const client = genko.apis;

$env/dynamic/privateは、このファイルがブラウザ側のコードから読まれるとビルドを止めます。.svelteからclientをimportすると次のエラーになります。

text
Cannot import $env/dynamic/private into code that runs in the browser,as this could leak sensitive information.

$env/static/privateではなく$env/dynamic/privateを使うのは、値をビルド時ではなく実行時に読むためです。ホスティング先の環境変数だけでビルドが通り、キーを交換したときも再ビルドが要りません。

GENKO_API_KEYは、サーバープロセスの環境変数として渡してください。 ページ送りの署名処理はprocess.env.GENKO_API_KEYを直接読むため、.envに書いただけでは足りません。開発時は--env-fileで読み込みます。

package.json
{  "scripts": {    "dev": "node --env-file=.env ./node_modules/vite/bin/vite.js dev"  }}
.env
GENKO_API_KEY=発行したキー

.envがGitの追跡対象になっていないことを確認してください。本番ではホスティング先の環境変数に同じ名前で設定します。ページ送りを使わない場合は.envだけでも動きますが、後から気づきにくいので最初から環境変数で渡すことをおすすめします。

記事一覧を出す

取得は+page.server.tsで行います。fetchGenkoListの戻り値は、そのままListClientのpropになります。

src/routes/blog/+page.server.ts
import { fetchGenkoList } from "@genko-me/svelte/server";import { client } from "$lib/client";import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async () => ({  list: await fetchGenkoList({    api: client.blog,    fields: ["title", "publishedAt"],    href: "/blog/{id}",    limit: 10,    orders: "-publishedAt",  }),});

fieldsにはListItemで表示するIDを並べます。ReactのようにJSXから集めることはできないため、ここで明示します。省略すると絞り込まず全フィールドを取得します。スキーマにないIDを書いた場合は設定エラーになります。hrefに含めた{id}も自動で取得対象に入ります。

src/routes/blog/+page.svelte
<script lang="ts">  import { ListClient, ListItem, Error, Null } from "@genko-me/svelte";
  let { data } = $props();</script>
<h1>ブログ</h1>
{#if data.list.status === "ok"}  <ListClient {...data.list.data} href={'/blog/{id}'}>    {#snippet children()}      <article>        <h2><ListItem id="title" /></h2>        <time><ListItem id="publishedAt" format="yyyy/MM/dd" timeZone="Asia/Tokyo" /></time>      </article>    {/snippet}    {#snippet empty()}<Null>公開されている記事はありません。</Null>{/snippet}  </ListClient>{:else}  <Error template />{/if}

childrenのsnippetが1件分のレイアウトです。empty・error・loading・paginationはそれぞれ専用のsnippetで渡し、繰り返しません。snippetを省くと、その状態では何も表示しません。

hrefテンプレートの{id}はSvelteの式と紛らわしいので、href={'/blog/{id}'}のように文字列として渡します。既定のタイムゾーンはUTCです。

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

記事詳細を出す

src/routes/blog/[id]/+page.server.ts
import { error } from "@sveltejs/kit";import { fetchGenkoEntry } from "@genko-me/svelte/server";import { client } from "$lib/client";import type { PageServerLoad } from "./$types";
export const load: PageServerLoad = async ({ params }) => {  const entry = await fetchGenkoEntry({    api: client.blog,    id: params.id,    fields: ["title", "body"],  });  if (entry.status === "empty") error(404, "記事が見つかりません。");  return { entry };};
src/routes/blog/[id]/+page.svelte
<script lang="ts">  import { EntryProvider, ViewItem, Error } from "@genko-me/svelte";
  let { data } = $props();</script>
{#if data.entry.status === "ok"}  <EntryProvider {...data.entry.data}>    {#snippet children()}      <article>        <h1><ViewItem id="title" /></h1>        <ViewItem id="body" />      </article>    {/snippet}  </EntryProvider>{:else}  <Error template />{/if}

fetchGenkoEntryは3つの状態を返します。okは取得できた状態、emptyは記事がない状態(404・不正な下書きキー)、errorは取得や設定に失敗した状態です。上の例ではemptyをSvelteKitのerror(404)に変換しています。Nullのsnippetでページ内に表示しても構いません。

オブジェクト形式のAPIではidを省略します。

本文の見出し・段落の間隔・画像の最大幅は、サイト側のCSSで指定します。ViewItemは標準の装飾を付けません。

css
article :global(h2) { margin-block: 2em 0.75em; font-size: 1.5em; }article :global(p) { margin-block: 1em; }article :global(img) { max-width: 100%; height: auto; }

表示されないとき

状況確認すること
一覧が空記事が保存済みで「公開」か。公開終了日時を過ぎていないか
statusがerrorになるサーバーのログに[genko.me]で始まる行が出ています。設定エラーはその内容、HTTPエラーはステータスが出ます
401APIキーが正しく有効期間内か。サーバープロセスでGENKO_API_KEYを読めているか
403APIキーを発行したワークスペースと接続先が同じか
404ワークスペースIDとエンドポイントblogが正しいか。キーでblogを許可しているか
createHmac is not exported by "__vite-browser-external".svelteから@genko-me/svelte/serverをimportしています。サーバー側のファイルへ移してください

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

最終更新: 2026年10月16日

目次