From dojo-kit
Error handling and API pipeline patterns. Use when creating any feature, page, or component that calls an API — including forms that submit data, pages that fetch and display data, or any code that talks to a server. Also use when writing functions that can fail, handling server responses, designing error types, or deciding between throwing and returning errors.
How this skill is triggered — by the user, by Claude, or both
Slash command
/dojo-kit:data-flowThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Functions that can fail return typed results. API operations follow a four-stage pipeline. Feature code never calls `fetch()` directly.
Functions that can fail return typed results. API operations follow a four-stage pipeline. Feature code never calls fetch() directly.
If dojo-kit.yaml exists at the project root, read it. Adapt the Consume stage examples to match libraries.dataFetching: react-query uses useQuery/useMutation, swr uses useSWR/useSWRMutation, apollo uses useQuery/useMutation from @apollo/client. If libraries.validation is set, use that library for request/response schemas in Define stage examples instead of the default zod. If libraries.httpClient is set (e.g., axios, ky, ofetch, got), the gateway uses that library. If not set or none, scaffold a fetchJson utility in platform/api/fetch-json.ts as the gateway's transport layer. The Result type, error types, and Factory/Unpack stages are library-agnostic and do not change.
type Result<T, E> =
| { success: true; data: T }
| { success: false; error: E };
All functions you write that can fail return Result<T, E>. The caller sees from the type that failure is possible and handles both paths explicitly.
| Situation | Use | Why |
|---|---|---|
| Business logic failure | Result | Caller decides how to handle |
| Broken invariant (impossible state) | throw | Let error boundary catch it |
Third-party code (fetch, JSON.parse) | try/catch at the boundary | Convert to Result immediately |
The boundary rule: try/catch wraps things that throw. Everything you write returns results. The boundary between thrown world and result world is thin, explicit, and low in the stack.
Two classes cover almost everything:
| Class | Fields | When to use |
|---|---|---|
ErrorBase | code, message, cause?, displayMessage? | All errors |
ValidationError | Extends ErrorBase + fieldErrors: Record<string, string> | Server-side field validation failures |
Error code format: CONTEXT_ACTION_CAUSE — e.g., CHECKOUT_SUBMIT_VALIDATION_ERROR, USER_GET_CURRENT_NETWORK_ERROR
Don't create NetworkError, TimeoutError, etc. Use ErrorBase with a specific code. The code is what consumers branch on.
Most web apps use REST with straightforward JSON responses. Use a simple three-file pattern:
| File | Role | Example |
|---|---|---|
.api.ts | Gateway functions — typed fetch wrappers | items.api.ts |
.queries.ts | React Query query hooks (grouped per domain) | items.queries.ts |
.mutations.ts | React Query mutation hooks (grouped per domain) | items.mutations.ts |
Gateway rule: feature code never calls fetch() directly. The .api.ts file handles request execution, auth injection, base URL, and logging. Auth is middleware — feature code doesn't think about tokens.
All API files colocate in the feature's api/ directory — gateway, queries, and mutations together:
features/<domain>/api/
<domain>.api.ts ← Gateway functions (all endpoints for this domain)
<domain>.queries.ts ← React Query query hooks: useItems, useSearchItems, etc.
<domain>.mutations.ts ← React Query mutation hooks: useCreateItem, useUpdateItem, etc.
API files promote from features/<domain>/api/ to platform/api/ only when multiple features need the same endpoints. Until then, keep them feature-scoped.
When response shapes are polymorphic or need explicit discrimination (GraphQL with partial errors, REST endpoints that return different shapes per status code), add an unpack stage:
| Stage | Responsibility | Testability |
|---|---|---|
| Define | Request shape (endpoint, document, types) — no logic | Type-checked at compile time |
| Unpack | Normalize every response variant into Result | Pure function — assert input → output |
| Factory | Wrap transport in try/catch, return typed operations | Mock the gateway, test with plain calls |
| Consume | Wire into framework (React Query hook, server loader) | Mock the factory |
Consult references/api-pipeline.md for the full four-stage pipeline specification. Use it when you copy-paste an API call, or a response shape change breaks something because the discrimination wasn't explicit.
references/errors.mdreferences/api-pipeline.mdResult for your code, try/catch only at third-party boundariesreferences/errors.md — Full error specification: Result type usage, ErrorBase/ValidationError definitions, error code conventions, error flow through system layersreferences/api-pipeline.md — API pipeline specification: define/unpack/factory/consume stages, gateway pattern, when to simplifynpx claudepluginhub murphyjoseph/claude-plugins --plugin dojo-kitDesigns RESTful API routes with Next.js App Router, Zod validation, auth guards, and typed responses. Activates when discussing API endpoints, route structure, or request/response schemas.
Implements standardized API error handling with RFC 7807 responses, typed error classes, middleware, and monitoring. Use for consistent HTTP errors across endpoints.
Enforces workflow for full-stack apps: requirements, architecture decisions, scaffolding checklists, patterns for API integration, auth, error handling, real-time (SSE/WebSocket) across Node/React/Next.js, Python, Go.