Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Deploy a handler

Serve a route from an already-built WebAssembly component. A handler is a function reached by an HTTP route — you declare it in project.cfg, validate the manifest, then sync, and the sync step validates the component blob and activates it against the site policy.

To build a component from scratch, see Write your first handler. To use the host bindings from guest code, see Use handler bindings. To run the same kind of component invoked by name instead of behind a route, see Deploy & invoke a function.

Before you start

  • A component built to the wasm32-wasip2 target that exports wasi:http/incoming-handler. Sync rejects a component without this export.
  • The component file reachable from your project root (here, dist/api.wasm).
  • A server built with the handlers feature.
  • The site policy enabled (handlers.enabled on the site) and its allow_imports covering every import you request — set this in step 3. sync does not set it, so a fresh site refuses a handler deployment until you do.

1. Declare the handler in project.cfg

Add the handler to the routing.handlers list. Each entry names a route pattern, the allowed methods, the component file, and the host imports it may use (sql, wasi:keyvalue, wasi:blobstore, wasi:messaging, invoke, plus wasi:http / wasi:io, which every handler gets):

routing: (
    handlers: [
        ( route: "/api/**", component: "dist/api.wasm",
          methods: ["GET", "POST"],
          imports: ["sql", "wasi:keyvalue"] ),
    ],
),

A component receives only the imports it declares here, and only those the site also grants. Unlisted interfaces (for example wasi:filesystem) are refused even when named.

A handler can also call a sibling top-level function in-process: grant it invoke and add an invoke_targets allowlist naming the functions it may reach (deny by default, * wildcards allowed). See Deploy & invoke a function and Use handler bindings.

2. Validate the manifest

Check the config shape and route table before you deploy:

boatramp validate
project.cfg: routing OK (1 handler: /api/** [GET, POST])

validate checks the manifest. The component blob itself — parseability, the wasi:http/incoming-handler export, and the import allowlist — is validated at sync.

3. Enable handlers on the site

The route you declared ships in the deployment, but a deployment that ships handlers is refused at activation unless the site permits them. That gate is the site’s handlers.enabled policy — a piece of site config separate from the deployment, and sync never sets it. Skip this step and the sync below fails with:

activation refused: deployment ships handlers/consumers but the site has them disabled

Enable it once with boatramp handlers. List the interfaces your handlers import (allow_imports must be a superset of every handler’s imports); the example /api/** handler above imports sql and wasi:keyvalue:

boatramp handlers enable --site my-site --allow sql --allow wasi:keyvalue

boatramp handlers show --site my-site prints the current policy; handlers disable turns it back off; handlers allow/deny <import>… adjust the allowlist. An import a handler requests but the site doesn’t grant is refused at activation — see Use handler bindings.

Or, declaratively, fold the same policy into an apply.cfg config.handlers block and run boatramp apply — it sets the site policy then deploys + activates, in the right order, so one command does the whole thing:

sites: [(
    name: "my-site",
    path: "./dist",
    routing: ( handlers: [( route: "/api/**", component: "dist/api.wasm",
                            methods: ["GET", "POST"], imports: ["sql", "wasi:keyvalue"] )] ),
    config:  ( handlers: ( enabled: true, allow_imports: ["sql", "wasi:keyvalue"] ) ),
)],

4. Sync the deployment

Upload the component and activate it:

boatramp sync ./dist --site my-site
validated dist/api.wasm — exports wasi:http/incoming-handler, imports OK
activated my-site -> 7f3a2b2c — handler /api/**

If the component requests an import the site does not allow, sync rejects the deployment and the previous one stays live.

5. Call the route

curl https://my-site.example/api/health
{"status":"ok"}

A method outside the handler’s methods list returns 405; a path outside the route pattern falls through to rewrites, then static content.

Reference