Multiple generators
Add TypeScript types and Tanstack Query hooks alongside the Zod validators from tutorial 01, and watch the cross-generator coordination converge.
What you'll build
A project running three stock generators against the same OpenAPI spec, producing:
- Zod validation schemas (from tutorial 01)
- TypeScript type aliases
- Tanstack Query
useQuery/useMutationhooks
The interesting part: the hooks reference the Zod schemas the validator generator produces — and the engine produces each schema exactly once even though both generators want it.
Prerequisites
- The
petstoreproject from tutorial 01. - Deno +
skmtcCLI installed.
Step 1: Install additional generators
skmtc install @skmtc/gen-typescript petstore
skmtc install @skmtc/gen-tanstack-query-fetch-zod petstoreVerify with skmtc list petstore --json — three generators should
be installed now.
Step 2: Inspect the dependency relationships
@skmtc/gen-tanstack-query-fetch-zod doesn't declare a hard
dependency on gen-zod or gen-typescript, but its generated
hooks reference Zod schemas (for validation) and TS types (for
parameters). When all three run together, they converge on shared
output via cross-generator coordination.
You don't need to configure anything for this — coordination is automatic.
Step 3: Regenerate
skmtc generate petstoreThe engine runs all three generators against the same parsed
document. New files appear in src/generated/:
Pet.generated.ts— now contains bothexport const pet = z.object({...})(from gen-zod) andexport type Pet = {...}(from gen-typescript). One file per schema component, both generators contributing.pet/useGetPetById.generated.ts(or similar) — the hook file, importingpetandPetfrom the schema file above.
Step 4: Verify the cross-generator coordination
Look inside a hook file:
import { pet, type Pet } from '../Pet.generated.ts'
export const useGetPetById = (args: { petId: number }) =>
useQuery({
queryKey: ['getPetById', args],
queryFn: () => fetch(`/pet/${args.petId}`).then(r => r.json()).then(pet.parse)
})The pet import is the same pet that gen-zod registered — not a
duplicate. If you run skmtc generate again, the output is
byte-identical: the engine is deterministic.
Try swapping the install order:
skmtc remove petstore @skmtc/gen-zod
skmtc remove petstore @skmtc/gen-typescript
skmtc install @skmtc/gen-tanstack-query-fetch-zod petstore # already installed
skmtc install @skmtc/gen-typescript petstore
skmtc install @skmtc/gen-zod petstore
skmtc generate petstoreThe output is identical. Generator order doesn't matter — see how idempotency works.
What just happened
Each generator's transform function ran against the same parsed
OasDocument. Internally:
gen-zodcalledinsertModel(ZodProjection, refName)for each schema → producedPet.generated.tswithexport const pet = ...gen-typescriptcalledinsertModel(TsProjection, refName)for each schema → producedexport type Pet = ...in the same filegen-tanstack-query-fetch-zodran per operation, and its Projection'stoString()calledinsertNormalizedModelfor the request/response schemas. Both calls landed on the same cache key(name, exportPath)as the standalone generators — the engine returned the existing definitions without creating duplicates.
Output is what each generator registered; nothing was deduplicated after the fact. The cache key made duplicates structurally impossible.
Next steps
- Tutorial 03: Customize with enrichments —
add per-operation overrides via
client.json - Recipe: Full-stack TypeScript app — add forms, mocks, and tables on top of this stack
- Cross-generator coordination concept — the deeper mechanism