Skip to content
Backlit
Esc
navigateopen⌘Jpreview
On this page

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.

Was this page helpful?