---
title: Request handling
description: 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`.

```ts title="middleware/require_session.ts"
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.
