Menu

Handler API

Site code is a JavaScript ES module. This page is the reference for the handler contract and the Web-style globals available to it. The capabilities on ctx (database, storage, secrets, mail, network) are in Context, the optional modules in SDK modules. Editor types are in sdk/microhost.d.ts.

Status. The SDK surface is shared by all three engines (Wasm, native, Linux worker). The cross-engine conformance suite (crates/engine-api/conformance, 48 scenario files) covers the handler contract, Request, Response, status and redirect rules, header rules, URL, TextEncoder, crypto.getRandomValues, import rules, limits and capability absence. It does not exercise microhost/sql, microhost/db or microhost/session, and not fetch or mail. microhost serve --dev serves static files only; handlers answer 501 there.

The handler

export default async function handle(request, ctx) {
  return Response.json({ method: request.method, path: new URL(request.url).pathname });
}

Request

request is created by the host. Request cannot be constructed by site code.

Member Value
method Uppercase HTTP method.
url Absolute URL https://<host header><decoded path><?query>. The scheme is always https.
headers A Headers with lowercase names.
params Frozen, null-prototype object of route captures; empty for exact and convention routes. See Routing.
text() Promise of the body decoded as UTF-8.
json() Promise of the parsed body. Throws on an empty or invalid body, so wrap it in try.
arrayBuffer() Promise of an ArrayBuffer.

Bodies are fully buffered and limited to 1 MiB by default (oversize requests are answered 413 by the edge and never reach your code). Methods without a body give an empty string.

Headers your code sees: the edge removes hop-by-hop headers, content-length, x-forwarded-*, forwarded and x-real-ip. There is no way to read the client IP. host and cookie are present. Header values that are not plain visible ASCII are dropped.

Request has no path or query members. Use new URL(request.url) (pathname, search).

Response

new Response(body?, { status?, headers? })
Response.json(value, init?)
Response.html(html, init?)
Response.redirect(url, status)

Host-side response checks (a violation is an invalid response, HTTP 502): header names must be legal tokens, values must not contain CR or LF, a Set-Cookie value is at most 4096 bytes, and the body must fit the response limit (4 MiB by default; above it the execution fails with HTTP 500). The host drops hop-by-hop headers (connection, keep-alive, proxy-*, te, trailer, transfer-encoding, upgrade) and content-length and recomputes framing.

Headers

new Headers(init?) accepts another Headers, an array of [name, value] pairs, or a plain object. Methods: get, has, set, append, delete. Names are case-insensitive. Values are converted with String().

const response = Response.redirect("/account", 303);
response.headers.append("Set-Cookie", "a=1; Path=/; Secure; HttpOnly");
response.headers.append("Set-Cookie", "b=2; Path=/; Secure; HttpOnly");

URL

new URL(input, base?) parses an absolute URL, or a relative one against base. It throws TypeError for an invalid URL. Read-only properties: href, origin, protocol, host, hostname, pathname, search, hash; toString() returns href. There is no URLSearchParams and no searchParams: search is the raw query string. If you parse queries yourself, decide how to treat repeated keys, +, and malformed percent escapes.

Text and bytes

Randomness and time

Console

console.log, info, warn, error write to the site log, never to the visitor. Strings print as is, Error objects as Name: message, other objects as JSON, arguments joined by spaces. Output is capped at 64 KiB per execution; later lines are dropped. Values your code obtained from ctx.env.get are redacted from console output. Do not log request bodies or credentials.

Modules

Handlers are ES modules with static imports:

import { escapeHtml } from "../lib/html.js";   // relative, ends in .js, stays in the release
import { users } from "microhost/db";          // reserved SDK module

Rules (details in Site layout): relative specifiers only (./ or ../), they must end in .js, and they must stay inside the release outside public/. Importing a public/ file fails with HTTP 403. Dynamic import(), eval and new Function are not available (a dynamic import fails with HTTP 500). Bare specifiers other than the four reserved SDK names fail. At most 256 modules and 8 MiB of module source per execution.

Not available

These are absent. Using them gives ReferenceError or undefined; do not polyfill them.

Errors thrown by host calls

Host calls (ctx.db, ctx.storage, ctx.env, ctx.mail, fetch) reject with an Error whose code is one of CAPABILITY_DENIED, QUOTA_EXCEEDED, INVALID_INPUT (a TypeError), HOST_UNAVAILABLE, HOST_INTERNAL. Messages are generic. SDK modules add their own lowercase codes. Full tables are in Limits and errors.