アプリを作る
アプリの構成
Hane の上に作るアプリを、画面・BFF・SDK・定義のディレクトリに分け、何を Hane に置き何をアプリに置くかを説明します。
このページでは、Hane の上に作る業務アプリの基本の形を説明します。参照アプリ(車両リース管理)と同じく、画面・BFF・定義の 3 つに分け、業務データと業務の決まりを Hane に置きます。
ブラウザ(画面) │ /bff/* だけを呼ぶ。cookie のセッション ▼BFF(アプリのサーバー) │ @hane/sdk で公開 API を呼ぶ。利用者のアクセストークン ▼Hane の公開 API(入口) │ ├─ 業務データ・Workflow・Task・Agent・文書・監査 └─ 定義(hanectl apply → ReleaseBundle)- 画面 は BFF の口だけを呼びます。Hane を直接呼ばず、API キーもアクセストークンも持ちません。
- BFF は、利用者のログインとセッションを持ち、Hane でトークン交換して得たアクセストークンで公開 API を呼びます。
- 定義 は Git に置くマニフェストで、hanectl で Hane に入れます。業務の決まり(計算・進め方・割り当て・Agent)はここに書きます。
ディレクトリの例
Section titled “ディレクトリの例”参照アプリは次のように分けています。自分のアプリも同じ形で始めると、BFF の書き方や定義の置き方をそのまま写せます。
ディレクトリmy-app/
ディレクトリapp/ 画面(React と Vite など)
ディレクトリsrc/
- api.ts BFF を呼ぶ口
- routes.tsx
ディレクトリbff/ アプリのサーバー
- app.ts 口(Hono など)。動かす場所によらない
- server.ts 手元の Node.js の起動とログイン
- worker.ts Cloudflare Workers の入口
ディレクトリdefinitions/ マニフェスト
- bundle.yaml ReleaseBundle に束ねる範囲
ディレクトリschemas/
- …
ディレクトリrules/
- …
ディレクトリworkflows/
- …
ディレクトリagents/
- …
ディレクトリpolicies/
- …
- package.json
BFF の口(app.ts)は動かす場所に依存させず、ログインとセッションの持ち方だけを server.ts や worker.ts で替えます。手元では開発用の IdP の PKCE とメモリのセッション、Cloudflare Workers では Cloudflare Access のトークンを使う、という分け方です。
BFF でトークン交換する
Section titled “BFF でトークン交換する”BFF は、利用者の IdP の id_token と、このアプリのサービスアカウントの API キーを Hane に渡して、アクセストークンに交換します。こうすると、利用者が元の主体、アプリが実行主体として記録され、Task を決めた人や値を確かめた人が利用者として残ります。
import { createHaneClient, unwrap } from "@hane/sdk";
export async function exchange(idToken: string) { const anon = createHaneClient({ baseUrl: process.env.HANE_API!, token: () => null }); return unwrap( anon.POST("/v1/auth/token", { body: { grant_type: "urn:ietf:params:oauth:grant-type:token-exchange", subject_token: idToken, subject_token_type: "urn:ietf:params:oauth:token-type:id_token", actor_token: process.env.HANE_API_KEY!, actor_token_type: "urn:hane:params:token-type:api_key", organization: "acme", project: "fleet", environment: "dev", }, }), );}交換したアクセストークンは 15 分で切れます。期限が近づいたら同じ id_token で交換し直し、id_token も切れかけていたらログインし直させます。要求ごとに、そのセッションのアクセストークンでクライアントを作ります。
const hane = (session: { accessToken: string }) => createHaneClient({ baseUrl: process.env.HANE_API!, token: () => session.accessToken });詳しくは 認証 を見てください。
何を Hane に置き、何をアプリに置くか
Section titled “何を Hane に置き、何をアプリに置くか”| Hane に置く | アプリに置く |
|---|---|
| 業務データ(レコード・値の状態・出典・関係)の正本 | 画面と画面の道筋 |
| 計算の規則、Workflow、スケジュール、割り当ての規則、Agent の定義 | 顧客の台帳の列から Hane の項目への変換(取り込みの前処理) |
| Task、承認、外への操作の記録、監査 | ログインとセッション(IdP との連携) |
| 会話、AgentRun、文書と検索の索引 | 画面の文言と見た目のテーマ |
- API キーとアクセストークンは BFF だけが持ち、ブラウザには渡しません。ブラウザとの間は httpOnly の cookie のセッションにします。
- 画面に出す項目名は、型の定義の
titleから引きます(GET /v1/data/types)。アプリの辞書に同じ名前を持ちません。 - Hane のエラーは、BFF で code と Problem をそのまま画面に返すと、項目ごとの誤りを画面で示せます。
- 画面の CSP は BFF が応答に付けます。外の origin から読むものを足したら CSP にも足します。
認証API キー、トークン交換、スコープ
SDKクライアント、エラー、冪等性、ページ送り
マニフェストを書く定義の種類とファイルの置き方
Cloudflare にデプロイするWorkers と静的なアセット、Access