Request handling
Use request context, middleware, transport, and responses.
Every app request uses the Kernel. It creates a RequestContext, runs
middleware, finds the resource definition, and calls the resolver or handler.
Middleware runs before endpoint lookup and parameter validation.
Context
The context exposes kind, resource, name, args, paramValues, and mode.
kind is view, zone, action, or lookup. A zone request uses the view name
in name. paramValues contains the declared parameters in a resolver.
Middleware must use args: parameter names are not available at that stage.
An action handler reads validated schema output from ctx.data. Middleware
reads the original payload. A lookup reads the search text from ctx.searchQuery.
View and zone resolvers read state with ctx.parse(scope).
mode is page, modal, or drawer. The kernel reads the lower-case
x-backlit-mode header for view, zone, and action requests. Missing or invalid
values use page. Lookup requests use page.
Middleware
Export a function with the Middleware type from @backlit/sdk/types.
Call await next() to continue. Return an error or redirect to stop the request.
Stopping without either response throws E_NO_RESPONSE.
import type { Middleware } from '@backlit/sdk/types'
const requireSession: Middleware = async (ctx, request, next) => {
if (!request?.cookies().session) {
return ctx.error(401, 'E_SESSION_REQUIRED')
}
await next()
return undefined
}
export default requireSession
The host must verify the session before it trusts a cookie value. This example
only shows the middleware response path. Register the module with
middleware: [() => import('./middleware/require_session.ts')] in defineApp.
A response from the resolver takes precedence over a response returned by
middleware after next(). Middleware can observe a request but cannot replace
an endpoint response after the endpoint runs.
Response helpers
| Helper | Result |
|---|---|
ctx.notify(textOrOptions) |
Success with a notification. Options accept text, title, and tone. |
ctx.redirectTo(view, args?) |
Redirect to a declared view. Middleware can also pass { resource, view }. |
ctx.refresh() |
Refresh the full page. |
ctx.refreshZones(names) |
Refresh the listed zones. An empty list refreshes the full page. |
ctx.error(status, code) |
Expected request error. |
ctx.fail(messageOrPaths, options?) |
Throw an action rejection. options.code replaces the default code. |
All response builders support .notify(...). Error responses also support
.setErrors([{ path, message }]). Omit path for a payload error. Nested field
paths use dots, such as lines.0.quantity.
An action that returns nothing succeeds with status 200. app.handleAction
converts schema failures and ctx.fail rejections to status 422. Unexpected
exceptions still throw. A view resolver returns content, a redirect, or an error.
Direct definition access
action.validate(input) returns the Standard Schema result.
action.execute(ctx) throws validation failures and handler rejections.
action.attempt(ctx) converts these expected failures to protocol errors.
Use the app methods for normal requests so middleware and address checks run.
Calling a definition directly skips these checks.
Transport
An optional transport has a required request member of type HttpRequest. Construct
it from the host’s parsed request accessors: query, headers, cookies,
json, and formdata. Each accessor runs once and its result is stored.
json and formdata return promises. Normalize header names to lower case.
The host parses the action body and passes it as payload to handleAction.
Use declaration merging to add application properties to RequestContext or
HttpRequest. Set the corresponding runtime values in middleware or the adapter.
A type declaration alone does not create a value.