skmtcdocs

Full-stack TypeScript app

Generate types, validators, query hooks, mocks, and forms from one OpenAPI document — the standard SKMTC combination for a TypeScript/React app.

What you'll build

A src/generated/ tree containing:

  • TypeScript types matching every schema component
  • Zod validators for those same schemas
  • Tanstack Query hooks (useQuery/useMutation) for every operation
  • MSW handlers for development-time mocking
  • shadcn/ui form components for mutating operations with object bodies

All five generators converge on shared definitions — the engine produces each schema exactly once, regardless of how many generators need it.

Stack

  • Vite or Next.js (your existing app, no SKMTC-runtime needed)
  • React + Tanstack Query + shadcn/ui
  • MSW (for dev-only mocks)

Generators used

Types: @skmtc/gen-typescript

Produces export type Pet = {...} per schema component. The foundation everything else references.

Validators: @skmtc/gen-zod

Produces export const pet = z.object({...}) per schema component. Used at runtime boundaries (API responses, form submissions).

Query hooks: @skmtc/gen-tanstack-query-fetch-zod

Produces useQuery for GET/DELETE and useMutation for POST/PUT/PATCH (when there's a request body). Hooks call fetch directly — typical first edit when cloned is replacing fetch with a team-specific wrapper.

Mocks: @skmtc/gen-msw

Produces per-operation http.get/http.post handlers plus a shared toRoutesList(deps) factory. Wired into the app's MSW worker for dev-mode mocking.

Forms: @skmtc/gen-shadcn-form

Produces a React form for every POST/PUT/PATCH operation with an object body. The form references both the Zod schema (for validation) and the mutation hook (for submit) — both already generated by the upstream generators.

Setup

skmtc init myapp src/generated
skmtc install @skmtc/gen-typescript myapp
skmtc install @skmtc/gen-zod myapp
skmtc install @skmtc/gen-tanstack-query-fetch-zod myapp
skmtc install @skmtc/gen-msw myapp
skmtc install @skmtc/gen-shadcn-form myapp

Edit .skmtc/myapp/.settings/client.json:

{
  "source": "https://api.example.com/openapi.json",
  "settings": {
    "basePath": "src/generated"
  }
}

Generate:

skmtc generate myapp

Step-by-step

What ends up where in src/generated/ (each generator's toExportPath claims its own subtree):

src/generated/
├── types/
│   ├── pet.generated.ts            # type + Zod schema (both generators converge on this file)
│   ├── order.generated.ts          # ditto
│   └── ...
├── services/
│   ├── useGetPetById.generated.ts  # query hook
│   ├── useAddPet.generated.ts      # mutation hook
│   └── ...
├── forms/
│   ├── AddPet.generated.tsx        # wired React form
│   └── ...
└── mocks/
    └── handlers.generated.ts       # all MSW handlers + toRoutesList factory

The hooks file imports from the types/Zod file:

// src/generated/services/useGetPetById.generated.ts
import { pet, type Pet } from '../types/pet.generated.ts'
import { useQuery } from '@tanstack/react-query'

export const useGetPetById = (args: { petId: number }) => useQuery({...})

The forms reference the hook and the Zod schema:

// src/generated/forms/AddPet.generated.tsx
import { pet } from '../types/pet.generated.ts'
import { useAddPet } from '../services/useAddPet.generated.ts'
// ...

Everything composes because all five generators share the (name, exportPath) cache. See cross-generator coordination concept.

Result

In your app:

import { useGetPetById } from '@/generated/services/useGetPetById.generated.ts'
import { AddPetForm } from '@/generated/forms/AddPet.generated.tsx'

export function PetDetailPage({ id }: { id: number }) {
  const { data } = useGetPetById({ petId: id })
  return (
    <div>
      <h1>{data?.name}</h1>
      <AddPetForm />
    </div>
  )
}

Set up MSW in development:

// src/setupMsw.ts
import { setupWorker } from 'msw/browser'
import { toRoutesList } from '@/generated/mocks/handlers.generated.ts'

const worker = setupWorker(...toRoutesList({ store: yourMockStore }))

Variations

  • Swap fetch transport. Clone gen-tanstack-query-fetch-zod to use your team's apiFetch wrapper. See how to swap a peer dependency.
  • Supabase backend. Use @skmtc/gen-tanstack-query-supabase-zod instead of the fetch variant. Add gen-shadcn-select and gen-shadcn-table for the search/list UI components that pair with it.
  • Add a table. Install @skmtc/gen-shadcn-table for list-GET operations.

Source

This recipe's stack is the most common SKMTC usage pattern. Real projects typically clone one or two of the generators (the fetch wrapper, the form's submit-flow) to match team conventions. The schema-level generators (gen-typescript, gen-zod, gen-msw) usually run unmodified.

See also

On this page