From armor
Enforces a strict OpenAPI-to-SDK boundary on TypeScript frontends: no raw fetch/HTTP calls, only imports from a generated typed SDK. Use when reviewing frontend API usage, regenerating clients, or auditing for drift.
How this skill is triggered — by the user, by Claude, or both
Slash command
/armor:sdk-boundaryThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The frontend ↔ backend contract is one thing: a strict OpenAPI spec. The
The frontend ↔ backend contract is one thing: a strict OpenAPI spec. The frontend imports a typed SDK generated from that spec — never raw HTTP. Backend changes flow to the frontend as TypeScript errors. That is the point.
A hand-written api.service.ts, api.client.ts, or http.ts that wraps
backend endpoints is a violation. Generated files live under one configured
output directory (typically src/api/), are named *.gen.ts, and start with a
"DO NOT EDIT — autogenerated" banner.
Check: imports of fetch, HttpClient, axios, ky, got, superagent,
or any hand-rolled API service module from outside the generated directory.
Fix: replace the call site with the corresponding SDK function. If the SDK doesn't expose the operation, the spec is wrong — fix the backend, not the frontend.
The autogeneration banner means hand edits survive exactly one regeneration and then disappear. Never edit generated files.
Check: git log -p against the generated directory for commits that aren't
"regenerate SDK".
Fix: move customization into a wrapper module that imports the SDK, or change the spec/config so the generator emits the desired output.
Without explicit operationIds on each endpoint, you get function names like
getUserOrdersApiV1UsersUserIdOrdersGet. Every
endpoint declares a camelCase identifier following REST verb conventions:
list*, get*, create*, update*, delete*.
Check: scan the spec for any operationId that's snake_case, repeats the
URL path, or contains an HTTP method suffix.
Fix (FastAPI shown; same idea wherever the spec is produced):
@router.get("/users", operation_id="listUsers")
@router.get("/users/{id}", operation_id="getUser")
anytsconfig.json has strict: true and disallows any. The whole point of the
SDK is that the type system catches breaking API changes; loose types silently
swallow them.
Check: tsconfig.json for "strict": true. Lint config disallows any.
Fix: tighten the config. The SDK already produces precise types; the project just has to honor them.
If the SDK is wrong, the spec is wrong. Editing generated code, casting away mismatches, or writing client-side adapters to "correct" the response shape hides drift between frontend and backend.
Check: any as, !, or // @ts-ignore adjacent to an SDK call site.
Fix: change the spec (and the backend serializer that produced it) so the type matches reality. Then regenerate.
Exactly one way to regenerate: npm run generate:api. The script invokes the
generator directly — no npx, no shell wrappers, no per-developer paths.
Check: package.json scripts has a single generate:api entry.
Fix: collapse alternatives into the canonical script.
Pin the generator's version. Document where the spec comes from — a running
backend URL or a checked-in openapi.json. If it's a live URL, the README says
the backend must be running for generate:api to work.
Check: package.json lists the generator in devDependencies with a
pinned version. The generator's config has a clear input.
Fix: pin the version. Document the source. If the spec is volatile, check in a snapshot.
Components, hooks, and services import from the SDK. They never import fetch,
HttpClient, axios, or any other HTTP client directly. Auth, base URL, error
handling, and interceptors live in the SDK's client config — exactly once.
Check: grep for HTTP-client imports outside the generated directory. For
brownfield projects, add a no-restricted-imports lint rule that flags them. A
documented exception wrapper (see Exceptions) is not a violation; an
undocumented one is.
Fix: move shared HTTP concerns into the SDK client setup. Replace each direct call with the SDK function for that operation.
Some endpoints never reach the SDK. SSE streams, WebSockets, long-poll, binary file downloads: OpenAPI 3.x can't describe them, so the generator skips them. Rule 5 doesn't apply. You can't fix a spec to cover what a spec can't express.
These are allowed, but kept on a short leash:
src/api/streaming.ts), never
a raw EventSource or fetch at a call site. Auth and base URL come from the
same client config the SDK uses.data shape can still
be an OpenAPI component. Import the generated type and parse into it.An unlisted raw fetch and a listed SSE wrapper look identical to a grep. The list is what keeps the boundary auditable.
The rules above don't depend on a specific generator. The setup below uses
@hey-api/openapi-ts.
npm install --save-dev @hey-api/openapi-ts@latest
openapi-ts.config.ts:
import { defineConfig } from '@hey-api/openapi-ts'
export default defineConfig({
input: 'http://localhost:8000/openapi.json',
output: 'src/api',
})
package.json:
{
"scripts": {
"generate:api": "openapi-ts"
}
}
Usage:
import { listUsers, getUser } from './api/sdk.gen'
const users = await listUsers({ query: { limit: 10 } })
const user = await getUser({ path: { user_id: 123 } })
For Angular projects, the Angular plugin generates HttpResource-based
services alongside requests and types
(https://heyapi.dev/openapi-ts/plugins/angular):
import { defineConfig } from '@hey-api/openapi-ts'
export default defineConfig({
input: 'http://localhost:8000/openapi.json',
output: 'src/api',
plugins: [
'@hey-api/client-fetch',
'@hey-api/typescript',
'@hey-api/sdk',
'@hey-api/angular',
],
})
Three layers come out:
HttpResource wrappers for Angular's reactive resource API.Components consume the generated resources or requests. Raw HttpClient stays
forbidden outside the generated directory — rule 8 applies unchanged.
For each violation found:
Sort by impact. A hand-edited generated file (rule 2) or a raw fetch at a
call site (rule 8) is higher priority than a single missing operation ID
(rule 3).
npx claudepluginhub markacianfrani/armor --plugin armorGuides creation, editing, and verification of skills for AI coding agents using test-driven development with subagent scenarios. Use when authoring or debugging skills.