API Reference

k.requestContext

Provide request-scoped values to every handler, typed, such as analytics ids, loggers, and the caller's locale.

Request-scoped values every handler receives, typed, such as analytics ids, loggers, and the caller's locale. Declare one with k.requestContext, give it the handler that fills it, and name it in defineConfig. Handlers read them under requestContext, keyed by name. See the Request Context guide for the full walkthrough.

A declaration that reads no headers takes the context schema on its own:

request-context.ts
export const logger = k
    .requestContext(
        z.object({
            requestId: z.string(),
        })
    )
    .handler(() => ({
        requestId: crypto.randomUUID(),
    }));

A declaration can also read request headers. They stay off the routes and out of the OpenAPI document, and the clients type them, so a caller sets them once and the resolver reads them validated.

request-context.ts
import { z } from 'zod';
import { k } from './k';

export const analytics = k
    .requestContext({
        headers: z.object({
            'x-posthog-session-id': z.string().optional(),
        }),
        context: z.object({
            sessionId: z.string().nullable(),
        }),
    })
    .handler(({ headers }) => ({
        sessionId: headers['x-posthog-session-id'] ?? null,
    }));
kizuna.config.ts
import { analytics } from './src/request-context';

export default defineConfig({
    adapter: expressAdapter(),
    routes,
    requestContext: {
        analytics,
    },
});
users.ts
listUsers: k.route({ ... }).handler(({ query, requestContext }) => {
    track(requestContext.analytics.sessionId, 'listUsers');
    // ...
}),

Sending values from a client

The fetch client takes the declared headers under requestContext and sends them with every request:

src/lib/api-client.ts
import { createClient } from './api-client.generated';

export const apiClient = createClient({
    baseUrl: 'https://api.example.com',
    requestContext: {
        'x-posthog-session-id': sessionId,
    },
});

The Swift and Kotlin clients take a RequestContext in their initializer:

APISetup.swift
let client = APIClient(
    baseURL: url,
    requestContext: .init(
        xPosthogSessionId: sessionId
    )
)
APISetup.kt
val client = APIClient(
    baseUrl = baseUrl,
    requestContext = APIClient.RequestContext(
        xPosthogSessionId = sessionId
    )
)

Resolvers run on every route, public ones included, before the guards, which read their values too, and never deny a request. They receive the adapter's native request objects, the route's params, and the declared headers (validated). The return is checked against the context schema. Request context never appears in the OpenAPI document.

On this page