# A backend without React

Build an API server with controllers, a typed client and realtime channels, and no React anywhere.

`kitr new my-api --kit server` creates a backend whose dependency graph contains no React, no TanStack and no Vite:

```
my-api/
  kitrrc.ts              # platforms: { api: server({ runtime: 'node' }) }, providers: logger, db, realtime
  server/main.ts         # createServer({ providers, config }) + routes + realtime + listen()
  server/routes.ts       # defineRoutes(...)
  server/controllers/    # plain classes, one per resource
  server/realtime.ts     # createRealtimeServer({ channels })
  config/                # one file per package; config/index.ts is generated by `kitr add`
  app/channels/          # defineChannel(...)
  app/env.ts             # createEnv({ server: { PORT, LOG_LEVEL, DATABASE_URL } })
  test/                  # createTestClient(server).get('/api/health')
```

Run it with `kitr dev` (tsx watch, or `bun --watch`, `deno run --watch`, `wrangler dev` depending on `runtime`) or `kitr build && kitr start`. `kitr config api runtime bun` switches runtime; `kitr config api port 8080` or `KITR_API_PORT=8080` changes the port. `kitr api:sync` writes `openapi.json` and a route table; `kitr routes:list` prints the routes.

## What you get

- **Router**: `defineRoutes` with groups, prefixes, named routes, `resource('projects', Controller)` for the five REST actions, per-route and per-group middleware (`cors()`, `rateLimit()`, `requireJson()`, your own `defineMiddleware`).
- **Controllers**: classes whose methods take an `HttpContext` (`params`, `query`, `body`, `request`, `response`, `locals`, `logger`, `requestId`, `app`). `static validation = { store: { body: schema } }` validates before the method runs and types the client.
- **Errors**: throw `KitrError.notFound('Project')`, `KitrError.validation(fieldErrors)` or anything else; every response has the same `{ code, message, status, fieldErrors? }` shape the client already understands.
- **Lifecycle**: providers run `register → boot → ready → shutdown`; `server.listen()` wires SIGINT/SIGTERM to `shutdown` with a timeout. The request-scoped container (`useRequestApp()`) forks the app per request.
- **Realtime**: `createRealtimeServer({ channels, authorize })` over WebSocket (crossws), Socket.IO or SSE, chosen in `config/realtime.ts`; `realtime.use(channel, params).emit(event, data)` from a controller. The client side (`kitr/realtime`) can live in any frontend, kitr or not.
- **Auth**: `kitr/auth/server` gives `authMiddleware({ verify })`, `requireUser()`, `currentUser()` and `can()` on the API side with the same `defineAbilities` the client uses.
- **Database**: `defineDb({ connect, disconnect })` in `config/db.ts` with the ORM of your choice; no ORM is bundled.
- **Typed client for the frontend**: `export type AppRoutes = typeof routes.$types` in the backend, `createApiClient<AppRoutes>()` in the frontend. Share the type through a workspace package or a `.d.ts`; there is no generated code to keep in sync.

## Mixing

A kitr React app that later needs its own backend keeps the same packages: `kitr add server api` adds the `api` platform, `server/main.ts` and `app/routes/api.$.ts`, which mounts the same router inside TanStack Start, so `kitr dev` runs both and `kitr dev site` runs just the UI. A standalone backend plus a `--kit app` frontend is the other valid shape; `kitr doctor` keeps React out of the backend either way.
