@kozou/api REST レイヤー
@kozou/api は Kozou 自前の REST レイヤーであり、v1.0 以降のデフォルト
バックエンド です。他のあらゆるサーフェスを駆動するのと同じ
Schema Context — あなたの CREATE TABLE、CREATE VIEW、COMMENT ON
文から構築されたもの — を与えると、手書きのルートコードなしで、
データベースのテーブルとビューを REST API として提供します。
このページでは、@kozou/api とは何でなぜ存在するのか、kozou dev の下で
どう起動するか (および外部 PostgREST へのオプトアウト方法)、生成される
エンドポイントの形、あなたの COMMENT テキストから導出される OpenAPI
ドキュメント、そして引かれるセキュリティ境界について説明します。
Kozou の出力群の中で REST がどこに位置するかについては、
3 つのサーフェス を参照してください。
説明文の背後にあるタグ文法については、
COMMENT 規約 を参照してください。
概要と存在理由
Section titled “概要と存在理由”それ以前の Kozou リリースでは、外部の PostgREST コンテナを配線して REST を
提供し、Admin UI はプラガブルなデータアダプター経由でそれと通信していました。
@kozou/api はその外部コンテナを Kozou 自身が所有するコードで置き換えます。
すなわち、CRUD エンドポイントと OpenAPI ドキュメントを Schema Context から
直接生成し、PostgreSQL 自体をクエリします。v1.0 以降はこれがデフォルトです —
kozou dev はこれをインプロセスで起動し、PostgREST はオプトアウトになりました
(下記の 起動方法 を参照)。
Admin UI は、すでに PostgREST 向けに使っているのと同じデータアダプターの
継ぎ目を通じて @kozou/api に到達します。そのため、データレイヤーの
切り替えは UI コードにとって破壊的変更ではありません。同じブラウザフローが、
どちらのバックエンドに対しても変更なしで動作します。
特に目立つ動機は 2 つあります。
- 可動部分が 1 つ減る。
@kozou/api(デフォルト) では、別個の PostgREST コンテナを実行する必要がありません。REST レイヤーはkozou devの 残りの部分とともにインプロセスで起動します。 - COMMENT ネイティブな OpenAPI ドキュメント。
@kozou/apiは Schema Context を読み取るため、出力する OpenAPI には、あなたがCOMMENTに書いた説明文、enum 値、AI ノート、ウィジェットヒントが 含まれます。下記の OpenAPI セクション を参照してください。
@kozou/api は kozou dev のデフォルトバックエンドです。
追加の設定なしで、kozou dev は @kozou/api を通じて REST をインプロセスで
提供します — PostgREST コンテナは不要です。
# Default: Admin UI + MCP, REST served in-process by @kozou/apikozou devkozou dev は 1 つのコマンドで 3 つのサーフェスを起動します。
| サーフェス | デフォルトポート | 備考 |
|---|---|---|
| Admin UI | 3333 | 生成された SvelteKit アプリ (@kozou/svelte-ui) |
| MCP HTTP | 3334 | AI エージェント向けの MCP サーバー (@kozou/mcp) |
@kozou/api | 3335 | 自前の REST レイヤー、127.0.0.1 にバインド |
Admin UI のサーバーサイドフェッチは (インプロセスの) @kozou/api に到達し、
@kozou/api が PostgreSQL に直接 SQL を発行します。
PostgREST へのオプトアウト
Section titled “PostgREST へのオプトアウト”代わりに外部 PostgREST を使うには、kozou.config.yaml
で adapter.type: postgrest を設定するか (そして PostgREST サービスを追加します —
スキャフォールドされた docker-compose.yml に例があります)、単発の実行に対して
--adapter postgrest で上書きします。
# Opt out of the in-house backend for one runkozou dev --adapter postgrestPostgREST を選択すると、Admin UI のサーバーサイドフェッチは、同じデータ アダプターの継ぎ目を通じて、代わりに PostgREST コンテナに到達します。
| フラグ | デフォルト | 説明 |
|---|---|---|
--adapter <kind> | api (adapter.type から) | REST バックエンド: api (自前、デフォルト) または postgrest (外部オプトアウト)。単発の実行に対して adapter.type 設定フィールドを上書きします。 |
--api-port <n> | 3335 | @kozou/api サーバーのポート (バックエンドが api のときに使用)。 |
# Move the @kozou/api port off 3335kozou dev --api-port 4000kozou dev のオプション一式については dev コマンド の
ページを、それが読む adapter.type 設定フィールドについては
kozou.config.yaml を参照してください。
REST エンドポイントの形
Section titled “REST エンドポイントの形”@kozou/api は、Schema Context 内の各テーブルとビューから、実行時に
エンドポイントを生成します。以下の例では、汎用的な products テーブル
(status カラムが draft / published / archived に制約されている)
と orders テーブルを使います。ご自身のリソース名に置き換えてください。
例ではデフォルトポートを前提とします。
B=http://127.0.0.1:3335サービス情報
Section titled “サービス情報”GET / はサービス情報と利用可能なリソースの一覧を返します。
curl -s "$B/" | jq一覧 — ページネーション、ソート、検索、フィルター
Section titled “一覧 — ページネーション、ソート、検索、フィルター”GET /<resource> はテーブルまたはビューの行を一覧します。以下をサポートします。
- ページネーション用の
page(1 始まり) とpageSize、 - 並び替え用の
sort=field.asc,other.desc(複数キー可)、 - テキストカラム横断のフリーテキスト
ILIKE用のsearch=<text>、 - 等価フィルター用の
<column>=<value>。
{ rows, total, page, pageSize } を返します。
# Published products, newest first, 20 per pagecurl -s "$B/products?page=1&pageSize=20&sort=created_at.desc&status=published" | jq
# Free-text search across text columnscurl -s "$B/products?search=keyboard" | jqフリーテキストの search はテキスト型のカラムを対象とします。
uuid カラムは対象から除外されます。PostgreSQL には uuid ILIKE text
の演算子が存在しないためです。
id による取得
Section titled “id による取得”GET /<resource>/<id> は、主キーによって 1 つの行を取得します。
その行、または 404 を返します。
curl -s "$B/products/42" | jqアイテムルート (取得、更新、削除) は主キーで行を指定するため、主キーを 持つテーブルが必要です。複合 主キーを持つテーブルは、キーが宣言する 順序でキー成分をカンマで結合して指定します。
# Composite key (tenant_id, id) = (7, 42)curl -s "$B/order_lines/7,42" | jq主キーを 持たない リソース — ビュー、または主キーのないテーブル — には アイテムルートがなく、一覧エンドポイントのみが利用できます。(複合キーの 値 自体はカンマを含めません。パスセグメントは URL デコード後にカンマで 分割されます。)
POST /<resource> は JSON ボディから行を作成し、201 と作成された行を
返します。PostgreSQL が自前で供給できるカラム (DEFAULT、サーバー生成値)
は省略できます。空のボディは、カラムのデフォルト値からなる行を挿入します。
curl -s -X POST "$B/products" \ -H 'content-type: application/json' \ -d '{"name":"Mechanical keyboard","status":"draft"}' | jqPATCH /<resource>/<id> は指定されたカラムを更新し、更新後の行、または
404 を返します。
curl -s -X PATCH "$B/products/42" \ -H 'content-type: application/json' \ -d '{"status":"published"}' | jqDELETE /<resource>/<id> は主キーによって削除し、削除された行、または
404 を返します。
curl -s -X DELETE "$B/products/42" | jqリレーション選択 — as=options 形式
Section titled “リレーション選択 — as=options 形式”リレーションピッカーを埋めるために、@kozou/api は、行全体の代わりに
{ id, label } ペアだけを返す軽量なルックアップを提供します。
GET /<resource>?as=options&label=<col>&fields=<a,b>&q=<text>&limit=<n>label— 各オプションの表示ラベルとして使うカラム。fields— 検索対象に追加するカラム。q— フリーテキストクエリ (ILIKEでマッチ)。limit— 返すオプションの最大数。
{ options: [{ id, label }] } を返します。
# Options for an orders form's "product" foreign keycurl -s "$B/products?as=options&label=name&fields=name,sku&q=key&limit=20" | jqこれは、外部キーのコンボボックスに入力したときに Admin UI が使う リレーション検索に対応します。
書き込みルールとエラー
Section titled “書き込みルールとエラー”- ビューは読み取り専用です。
CREATE VIEWは一覧エンドポイントとして のみ公開されます — ビューは主キーを持たないため、アイテムルートはありません。 ビューへの書き込みは405を返します。 - 未知のカラムは拒否されます。 テーブルに存在しないカラムを指定した
作成または更新ボディは
400を返します。 - 未知のリソース は
404を返します。
RPC アクション — POST /rpc/<schema>.<fn>
Section titled “RPC アクション — POST /rpc/<schema>.<fn>”上記のテーブル・ビューの CRUD に加えて、v1.4 以降、@kozou/api は
スキーマの動詞もコンパイルします。COMMENT に @expose: rpc タグを
持つ Postgres 関数は、POST /rpc/<schema>.<fn> で呼び出せるアクションになり、
引数は JSON ボディから取られます。同じアクションは同時にあらゆる場所で
表面化します — ここでの REST、OpenAPI ドキュメント、
MCP の describe_functions ツール、そして Admin UI の「Actions」ページです。
# approve_order(order_id uuid) アクションを実行curl -s -X POST "$B/rpc/public.approve_order" \ -H 'content-type: application/json' \ -d '{"order_id":"…"}' | jq公開はオプトインであり、暗黙のうちに行われることはありません。呼び出し元が
実際にアクションを実行できるかどうかは、リクエストのロールにおける PostgreSQL の
EXECUTE 権限で強制されます。RPC のワイヤーシェイプは Kozou v1.6 時点で
安定した契約で、テーブル・ビューの CRUD サーフェス (v1.0 以来安定) と同様です。詳しい
ルールは RPC アクション を参照してください。
/openapi.json の OpenAPI ドキュメント
Section titled “/openapi.json の OpenAPI ドキュメント”GET /openapi.json は API 全体に対する OpenAPI 3.1 ドキュメントを返します。
これを特別なものにしているのは、その説明文があなたのデータベースの
COMMENT テキストから来ているという点です。これが、自前レイヤーを
際立たせる COMMENT ネイティブな OpenAPI です。
# The document version and the schema component namescurl -s "$B/openapi.json" | jq '.openapi, (.components.schemas | keys)'
# One table's schema (description, x-kozou-ai, enum, x-kozou-widget)curl -s "$B/openapi.json" | jq '.components.schemas["public.products"]'Schema Context からドキュメントへのマッピングは次のとおりです。
- テーブル、ビュー、カラムの説明 (
COMMENTの散文本体) はスキーマのdescriptionになります。 @aiノート はx-kozou-ai拡張になります。CHECKリストとENUMメンバー はenumになります。- 解決済みのウィジェット は、JSON Schema の
type/formatと並んでx-kozou-widget拡張になります。
NULL 許容のカラムは型のユニオンとして出力されます — 例えば
["string", "null"] です。主キーを持つテーブルには一覧、作成、取得、更新、
削除のパスが付き、主キーを持たないテーブルには一覧と作成のみ (アイテム
ルートなし)、ビューには読み取り専用の一覧パスが付きます。
次のように書かれた products の status カラムは、
COMMENT ON COLUMN products.status IS 'Publication state of the product. @ai: Only ''published'' rows should appear in public listings. @widget: enum-select';ドキュメント内では、“Publication state of the product.” という
description、draft / published / archived の enum
(カラムの CHECK リストから)、@ai テキストを運ぶ x-kozou-ai ノート、
そして enum-select の x-kozou-widget として現れます。完全なタグ文法 —
@ai、@widget、@policy、@example — については、
COMMENT 規約 を参照してください。
@policyテキストは@kozou/coreによってパースされ、OpenAPI ドキュメント にはx-kozou-policy拡張 (テーブル/ビューおよびカラムのスキーマ上) として 表出されます。これは AI エージェントとクライアント向けの advisory な メタデータであり、@kozou/apiはこれを強制しません。アクセス制御は あなたの PostgreSQL の行レベルセキュリティの仕事です。下記の セキュリティ境界 を参照してください。
セキュリティ境界
Section titled “セキュリティ境界”@kozou/api は デフォルトでループバック上のゼロ認証 で動作し、設定すると
opt-in の JWT + RLS レイヤーが加わります。そのデフォルトは、信頼された境界 —
ローカル開発、またはプライベートな compose ネットワーク — 向けに作られています。
- デフォルトでループバック。 サーバーは
127.0.0.1にバインドします。kozou dev経由で実行する場合、同じホスト上の Admin UI の サーバーサイドフェッチからのみ到達されます。サーバーが非ループバックの ホストにバインドされた場合は、目立つ警告が出力されます。 - 設定しない限りゼロ認証。
auth設定がなければ、認証レイヤーは ありません — 信頼された境界を越えて公開しないでください。
実行場所にかかわらず成り立つ安全性の特性が 2 つあります。
- 識別子の許可リスト化。 テーブル、ビュー、カラムの識別子は、クエリが 構築される前に、イントロスペクションされた Schema Context — 許可リスト — に対して検証され、防御的にクオートされます。
- パラメータ化された値。 ユーザー提供のすべての値はパラメータ化された クエリ引数として渡されます。値が SQL テキストに補間されることはありません。
opt-in の JWT + RLS
Section titled “opt-in の JWT + RLS”auth 設定を渡すと (kozou.config.yaml
を参照)、@kozou/api は各リクエストの署名付き JWT (HS256 / RS256 / リモート
JWKS) を検証し、そのリクエストを request.jwt.claims 経由で公開される claim
とともに SET LOCAL ROLE <claim の role> の下のトランザクション内で実行します。
そして、あなたの PostgreSQL の行レベルセキュリティ (RLS) ポリシー が、
各リクエストが何を読み書きできるかを決めます。token のないリクエストは、
匿名ロール (anonRole) を設定していない限り 401 で拒否されます。設定して
いる場合は、空の claim でそのロールの下で実行されます。存在するが無効な
token は常に 401 です。
Kozou は token を検証して role を切り替えるだけで、identity の発行 (ユーザー登録・ログイン・トークン発行) は外部プロバイダに委譲します — 認証と認可 を参照してください。
次に読むべきもの
Section titled “次に読むべきもの”- 3 つのサーフェス — REST API が Admin UI と MCP コンテキストとどう並ぶか。
- COMMENT 規約 — OpenAPI の説明文を
形づくる
@ai、@widget、@policy、@exampleタグ。 - dev コマンド —
--adapterを含む、kozou devのオプション一式。 kozou.config.yaml— バックエンドが 読むadapterとauthの設定。