Workflow adapter handler
Skill marzun9620/agent_skills/workflow/skills/workflow-adapter-handler
My personal Claude Code skills — 59 of them. Use any, fork the repo, or contribute yours. Install with /plugin marketplace add marzun9620/agent_skills
npx -y skills add marzun9620/agent_skills --skill workflow-adapter-handlerAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
HTTP Handler implementation guide (Hono + OpenAPI). Only the adapter layer is allowed to call Effect.runPromise. Use describeRoute for OpenAPI definitions and Presenter for Domain → DTO conversion. Triggers: API endpoint creation, Hono handler, HTTP adapter, REST API, OpenAPI, describeRoute, routing.
SKILL.md
6.7 KB, ~1.8k tokens by cl100k_base, as published. Nobody here has run it
HTTP Handler 実装手順
ADR-0004 / ADR-0006 準拠。adapter 層のみが Effect ↔ Promise のブリッジを担当。
Import ルール(重要)
全ての cross-directory import は barrel(index.js)経由(ADR-0004 §7):
// ✅ 正しい
import { Entity, EntityId } from "~/domain/{module}/index.js";
import { findEntity } from "~/usecase/{module}/index.js";
import { notFound, internalServerError } from "~/packages/server/index.js";
// ❌ 禁止
import { findEntity } from "~/usecase/{module}/findEntity.js";
adapter/http 内部の import は submodule root barrel 経由:
// ✅ adapter/http 内部
import type { EffectRuntimeEnv } from "~/adapter/http/middleware/index.js";
1. Response Schema 定義
ファイル: apps/datahub/src/adapter/http/schemas/{resource}.ts
import { Schema } from "effect";
import { paginatedResponse } from "./common.js";
const EntityItemSchema = Schema.Struct({
id: Schema.String,
name: Schema.String,
status: Schema.Literal("ACTIVE", "ARCHIVED"),
}).annotations({
identifier: "EntityItem",
description: "Entity summary",
});
export const EntityDetailResponse = Schema.standardSchemaV1(
Schema.Struct({
id: Schema.String,
name: Schema.String,
status: Schema.Literal("ACTIVE", "ARCHIVED"),
}).annotations({
identifier: "EntityDetailResponse",
description: "Detailed entity",
}),
);
export const EntityListResponse = paginatedResponse(EntityItemSchema, "EntityListResponse");
Schema.standardSchemaV1()でラップ.annotations({ identifier, description })で OpenAPI 情報付与- ページネーションは
paginatedResponseヘルパーを使用
2. Presenter 定義
ファイル: apps/datahub/src/adapter/http/presenters/{resource}Presenter.ts
import { Entity } from "~/domain/{module}/index.js";
export const toEntityDetailResponse = (entity: Entity) => ({
id: entity.id,
name: entity.name,
status: entity.status,
});
export const toEntityItemResponse = (entity: Entity) => ({
id: entity.id,
name: entity.name,
status: entity.status,
});
- Domain Entity → HTTP DTO の変換
- snake_case のフィールド名(HTTP API 規約)
3. Route Handler 定義
ファイル: apps/datahub/src/adapter/http/routes/{resource}.ts
import { Hono } from "hono";
import { describeRoute } from "hono-openapi";
import { resolver } from "hono-openapi/effect";
import type { EffectRuntimeEnv } from "~/adapter/http/middleware/index.js";
import { EntityDetailResponse } from "~/adapter/http/schemas/{resource}.js";
import { toEntityDetailResponse } from "~/adapter/http/presenters/{resource}Presenter.js";
import { findEntity } from "~/usecase/{module}/index.js";
import { EntityId } from "~/domain/{module}/index.js";
import { notFound, internalServerError } from "~/packages/server/index.js";
export const entitiesRoute = new Hono<EffectRuntimeEnv>()
.get(
"/entities/:id",
describeRoute({
summary: "Get entity by ID",
tags: ["Entities"],
security: [{ "X-User-Context": [] }],
responses: {
200: {
description: "Entity found",
content: { "application/json": { schema: resolver(EntityDetailResponse) } },
},
404: { description: "Entity not found" },
},
}),
async (c) => {
const id = EntityId.make(c.req.param("id"));
const result = await c.var.runAuthenticated(
findEntity(id).pipe(
Effect.catchTags({
EntityNotFoundError: () =>
Effect.fail(notFound({ detail: "Entity not found", code: "ENTITY_NOT_FOUND" })),
RepositoryError: () =>
Effect.fail(internalServerError({ code: "INTERNAL_ERROR" })),
}),
Effect.map(toEntityDetailResponse),
),
);
return c.json(result);
},
);
c.var.runAuthenticated()で認証済み Effect 実行c.var.runHttp()で認証不要の Effect 実行Effect.catchTagsで domain/infra エラーをHttpErrorに変換- HttpError の
codeはProblemInit.codeで直接指定(extensionsは使わない)
4. Route 登録
ファイル: apps/datahub/src/setup.ts
// privateRoutes に追加(認証が必要な場合)
privateRoutes.route("/api/v1", entitiesRoute);
// apiRoutes にも追加(OpenAPI spec 生成用)
apiRoutes.route("/api/v1", entitiesRoute);
5. Barrel Export
ファイル: apps/datahub/src/adapter/http/routes/index.ts に追加
ファイル: apps/datahub/src/adapter/http/schemas/index.ts に追加
ファイル: apps/datahub/src/adapter/http/presenters/index.ts に追加(必要に応じて作成)
6. HTTP Test
ファイル: apps/datahub/tests/adapter/http/{resource}.test.ts
import { app } from "../../helpers/testApp.js";
describe("GET /api/v1/entities/:id", () => {
it("returns 200 with entity detail", async () => {
const entity = await EntityFactory.create(getDb());
const res = await app.request(`/api/v1/entities/${entity.publicId}`, {
headers: { "X-User-Id": testUserId },
});
expect(res.status).toEqual(200);
const body = await res.json();
expect(body.id).toEqual(entity.publicId);
});
it("returns 404 when entity not found", async () => {
const res = await app.request("/api/v1/entities/non-existent", {
headers: { "X-User-Id": testUserId },
});
expect(res.status).toEqual(404);
});
});
テスト踏襲ルール
実装前に、plan の Ref test に指定された既存テストファイルを読むこと:
- 基本:
tests/adapter/http/内の既存テストを参考にする app.request()でHTTPリクエストを送信するパターンを踏襲- ステータスコード + レスポンスボディの両方を検証
- 認証ヘッダー(
X-User-Id)の有無でケースを分ける expect(res.status).toEqual(200)を使う
チェックリスト
-
Effect.runPromiseは adapter 層のみ(c.var.runHttp/c.var.runAuthenticated経由) -
describeRouteで OpenAPI 定義 - Domain エラー → HttpError の変換が
Effect.catchTagsで行われている - HttpError の
codeはProblemInit.codeで直接指定 - Presenter で Domain → DTO 変換
-
setup.tsに route 登録 - barrel export が存在
- PII がログに含まれていない(ADR-0006)