Request API

primate/request exports the application-visible request type received by route handlers, hooks, layouts, and request-aware module hooks.

import type { RequestFacade } from "primate/request";

This entrypoint has no runtime default export. RequestBody, RequestBag, and RequestView below describe types exposed through RequestFacade; they are not independent exports from primate/request. See the Requests guide for workflows and longer examples.

RequestFacade

type RequestFacade = {
  method: Method;
  body: RequestBody;
  cookies: RequestBag;
  headers: RequestBag;
  original: Request;
  path: RequestBag;
  query: RequestBag;
  target: string;
  url: URL;

  forward(to: string, headers?: Dict<string>): Promise<Response>;
  has(key: string): boolean;
  try<T>(key: string): T | undefined;
  get<T>(key: string): T;
  set<T>(
    key: string,
    value: T | ((prev: T | undefined) => T),
  ): RequestFacade;
  delete(key: string): RequestFacade;
  toJSON(): RequestView;
};

Route schemas supplied through route.with() narrow the body, path, and query properties before the handler runs.

request.method

The normalized lowercase HTTP method.

request.method: Method;

See the supported Method union.

request.body

Content-type-aware access to the incoming body.

request.body: RequestBody;

Use exactly one reader from RequestBody. With a body schema, the reader matching the route's declared content type returns the schema's awaited inferred type.

request.path

Decoded dynamic route parameters.

request.path: RequestBag;

Path keys are normalized to lowercase. raw is the request pathname. A path schema changes the bag's generic value type.

request.query

Query-string values.

request.query: RequestBag;

Keys are case-insensitive and normalized to lowercase. Repeated keys keep the last value; raw preserves the original search string, including its leading ?. A query schema changes the bag's generic value type.

request.headers

Request headers as a case-insensitive RequestBag.

request.headers: RequestBag;

Header keys are lowercase. Values reflect the runtime's WHATWG Headers normalization before the bag is created. The bag's raw string is empty; use request.original.headers when the native Headers interface is required.

request.cookies

Cookies parsed from the Cookie header.

request.cookies: RequestBag;

Cookie names and values are URL-decoded when possible. Names are case-sensitive, a repeated name keeps its last value, and raw is the original Cookie header or "".

request.original

The original platform WHATWG request.

request.original: Request;

Use it for native operations such as clone(), formData(), direct stream access, or the abort signal. Request-context values are not added to it.

request.target

The request target composed from pathname and query string.

request.target: string;

For https://example.test/search?q=primate, the value is "/search?q=primate".

request.url

The parsed absolute request URL.

request.url: URL;

request.forward()

Forwards the original request method and body to another URL.

forward(to: string, headers?: Dict<string>): Promise<Response>;

Returns: The upstream native Response.

The body is the original request stream. Forward before consuming it through a body reader, and disable route body parsing when the route is only a proxy.

request.has()

Checks whether the request context contains a key.

has(key: string): boolean;

This tests key presence, even when the stored value is undefined.

request.try()

Reads a typed request-context value without requiring it to exist.

try<T>(key: string): T | undefined;

The generic is a caller assertion; context keys do not carry a shared runtime schema.

request.get()

Reads a required typed request-context value.

get<T>(key: string): T;

Throws: Error("Missing context key: …") when the key is absent. A present key whose value is undefined does not throw.

request.set()

Stores or updates a request-context value and returns the same facade for chaining.

set<T>(key: string, value: T): RequestFacade;
set<T>(
  key: string,
  update: (prev: T | undefined) => T,
): RequestFacade;

The callback form receives the previous value or undefined. Context is request-scoped, server-only, and may contain non-serializable values.

request.delete()

Removes a request-context key and returns the same facade.

delete(key: string): RequestFacade;

Deleting a missing key has no effect.

request.toJSON()

Returns the client-safe request view used by rendering and hydration.

toJSON(): RequestView;

Only normalized path, normalized query, and url are included. Body, headers, cookies, context, method, target, and the original request are omitted.

RequestBody

The body-reader type exposed as request.body.

declare class RequestBody {
  json(): Promise<JSONValue>;
  form(): Promise<Record<string, string>>;
  formEntries(): Promise<[string, string][]>;
  multipart(): Promise<{
    form: Record<string, string>;
    files: Record<string, File>;
  }>;
  text(): Promise<string>;
  blob(): Promise<Blob>;
}

Every reader verifies the incoming media type and consumes the body once. Calling a reader for a different content type or trying to parse the body again throws. Media-type parameters such as a multipart boundary are ignored during the comparison. Native decoding failures and schema parse failures reject the returned promise; a Pema parse failure in a route handler becomes Primate's 400 JSON error response.

RequestBody.json()

json(): Promise<JSONValue>;

Requires application/json. It uses the native Request.json() parser and, when configured, returns the body schema's parsed value.

RequestBody.form()

form(): Promise<Record<string, string>>;

Requires application/x-www-form-urlencoded. Repeated names keep their last value. File entries are rejected because URL-encoded forms must contain text.

RequestBody.formEntries()

formEntries(): Promise<[string, string][]>;

Requires application/x-www-form-urlencoded. Unlike form(), it preserves repeated fields and submission order. This raw entry reader does not apply the route body schema.

RequestBody.multipart()

multipart(): Promise<{
  form: Record<string, string>;
  files: Record<string, File>;
}>;

Requires multipart/form-data. Text and file fields are separated by name; repeated names keep the last value. A configured body schema parses the combined { form, files } object.

RequestBody.text()

text(): Promise<string>;

Requires text/plain and applies the configured body schema when present.

RequestBody.blob()

blob(): Promise<Blob>;

Requires application/octet-stream and applies the configured body schema when present.

RequestBag

Normalized key/value storage used for path, query, headers, and cookies.

declare class RequestBag<T extends Dict = Dict<string>>
  implements Iterable<[keyof T & string, T[keyof T & string]]> {
  get raw(): string;
  get size(): number;
  [Symbol.iterator](): IterableIterator<[
    keyof T & string,
    T[keyof T & string],
  ]>;
  get<K extends keyof T & string>(key: K): T[K];
  try<K extends keyof T & string>(key: K): T[K] | undefined;
  has<K extends keyof T & string>(key: K): boolean;
  toString(): string;
  toJSON(): T;
}

RequestBag instances also implement Pema's parseable protocol, so a Pema object schema can parse a bag directly.

RequestBag.raw

The untouched source string associated with the bag.

get raw(): string;

See each facade property above for its source value.

RequestBag.size

The number of normalized keys, including keys whose value is undefined.

get size(): number;

RequestBag[Symbol.iterator]()

Iterates normalized [key, value] pairs and skips entries whose value is undefined.

[Symbol.iterator](): IterableIterator<[
  keyof T & string,
  T[keyof T & string],
]>;

RequestBag.get()

Returns a defined value after applying the bag's key normalization.

get<K extends keyof T & string>(key: K): T[K];

Throws: When the normalized key is absent or its value is undefined.

RequestBag.try()

try<K extends keyof T & string>(key: K): T[K] | undefined;

Returns undefined for an absent key or a key whose value is undefined.

RequestBag.has()

has<K extends keyof T & string>(key: K): boolean;

Returns true only when the normalized key exists with a defined value.

RequestBag.toString()

Returns raw, which also makes template interpolation use the original source string.

toString(): string;

RequestBag.toJSON()

Returns a shallow null-prototype clone of the normalized contents.

toJSON(): T;

RequestView

The value returned by request.toJSON().

type RequestView = {
  path: PartialDict<string>;
  query: PartialDict<string>;
  url: URL;
};
Previous
Route
Next
Response