Core Concepts
The mental model behind Katman — context, procedures, middleware, and type inference.
Before diving into API details, it helps to understand how Katman thinks about your code. There are four main ideas.
Context
Every request starts by running your context factory. This is a function you provide when creating the Katman instance. It receives the raw HTTP request and returns an object.
import { } from "katman"
const = ({
: () => ({
: getDB(),
: .(.),
}),
})This object — called ctx — is available in every procedure and every guard. Think of it as the shared state for one request.
Guards can add to the context. If a guard returns { user }, then ctx.user becomes available in all procedures that use that guard.
Procedures
A procedure is a single API function. It takes an input, does something, and returns an output. There are three types:
| Type | What it does |
|---|---|
query | Reads data. Like a GET request. |
mutation | Writes data. Like a POST request. |
subscription | Streams data over time. Uses Server-Sent Events. |
See the Procedures page for how to write them.
Middleware
Middleware runs before (and sometimes after) a procedure. Katman has two kinds:
Guards run before the procedure and can add to the context:
const = k.guard(async () => {
const = await verifyToken(.headers.authorization)
if (!) throw new KatmanError("UNAUTHORIZED")
return { } // added to ctx
})Wraps run before and after, like an onion:
const = k.wrap(async (, ) => {
const = .()
const = await ()
.(`Took ${.() - }ms`)
return
})See the Middleware page for details and execution order.
Type inference
Katman infers types from your code. You don't write them yourself and you don't run a code generator.
When you define a procedure with a Zod schema, the client automatically knows the input type. When the procedure returns something, the client knows the return type. When a guard adds { user } to the context, the procedure knows ctx.user exists.
This works because of InferClient:
import type { } from "katman"
type = <typeof appRouter>
// Client.users.list: (input: { limit?: number }) => Promise<User[]>Katman uses the Standard Schema spec for validation. This means Zod, Valibot, and ArkType all work — you're not locked into any one library.