# Full-stack TypeScript app



## What you'll build [#what-youll-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 [#stack]

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

## Generators used [#generators-used]

### Types: `@skmtc/gen-typescript` [#types-skmtcgen-typescript]

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

### Validators: `@skmtc/gen-zod` [#validators-skmtcgen-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` [#query-hooks-skmtcgen-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` [#mocks-skmtcgen-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` [#forms-skmtcgen-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 [#setup]

```bash
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`:

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

Generate:

```bash
skmtc generate myapp
```

## Step-by-step [#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:

```ts
// 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:

```tsx
// 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](/docs/concepts/cross-generator-coordination).

## Result [#result]

In your app:

```tsx
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:

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

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

## Variations [#variations]

* **Swap fetch transport.** Clone `gen-tanstack-query-fetch-zod`
  to use your team's `apiFetch` wrapper. See [how to swap a peer
  dependency](/docs/authoring/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 [#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 [#see-also]

* [Tutorial 02: Multiple generators](/docs/using/tutorials/02-multiple-generators) —
  the entry-point walkthrough for this combination
* [Cross-generator coordination concept](/docs/concepts/cross-generator-coordination)
* [Stock generators overview](/docs/reference/stock-generators/overview)
