Skip to content
QueryWeave

Server

Adapter

Web-standard `Request` and `URL` helpers for request-scoped decoding.

Package
@queryweave/server
Runtime
Any web-standard server, edge, or worker runtime
Depends on
@queryweave/core
Framework required
No
InstallQuick examplePackage: @queryweave/server

This package decodes the query of a Request or a URL with a model, builds a canonical query string or a complete URL from typed state, and gives you a read-only QuerySource scoped to one request.

It cannot navigate. A request is read once and then ends, so there is no history to push to. That is why the package returns a QuerySource instead of a QueryAdapter: the navigation methods are missing from the type, so nothing can call them and fail at runtime.

Request-scoped source — reads once, never navigates

Request-scoped source — reads once, never navigates

  1. Request (incoming)
  2. QuerySource (read-only)
  3. QueryModel.decode() (typed result)

Request → QuerySource. QuerySource → QueryModel.decode().

Read, decode, respond. Nothing in this chain can navigate.
Terminal window
pnpm add @queryweave/core @queryweave/server

No Node built-in is used anywhere in the package, so it runs unchanged on Node.js, Deno, Bun, Cloudflare Workers, and any other runtime with Request and URL. Node.js is the runtime the repository tests on; the others are supported by construction, not by a fixture — see Support and stability.

import { readRequestQuery } from "@queryweave/server";
export async function handler(request: Request): Promise<Response> {
const result = readRequestQuery(request, products);
const values = result.ok ? result.value : { ...products.defaults(), ...result.partial };
return Response.json({
products: await search(values),
issues: result.issues,
});
}

The merge on the second line is the standard pattern: render something useful, and keep the issues for diagnostics.

readUrlQuery(url, model); // string | URL
readRequestQuery(request, model); // Request
await readUrlQueryAsync(url, model); // when validation is asynchronous
await readRequestQueryAsync(request, model);

Only the query is ever read, and it is read from the string itself — the part between the first ? and any # — so a relative path, an absolute URL, or a request line with an unusual host all work and none of them throws. relativeUrlBase (http://queryweave.invalid) is the origin createQueryUrl uses for a base that is not absolute, exported so tests can assert against it.

const result = readUrlQuery("/products?search=vue&page=2", products);
result.ok; // true
if (result.ok) result.value; // { search: "vue", page: 2, ...defaults }
import { createRequestQuerySource } from "@queryweave/server";
const source = createRequestQuerySource(request);
source.read(); // "?page=2"
source.request; // the original Request

Use this when a helper wants a QuerySource rather than a decoded result — for example, code that should work with any read-only environment.

import { createQueryUrl, encodeQuery } from "@queryweave/server";
encodeQuery(products, { search: "vue", page: 2, sort: "created_at", status: "all" });
// "search=vue&page=2"
createQueryUrl("https://example.com/products?utm_source=email", products, values);
// https://example.com/products?search=vue&page=2&utm_source=email

encodeQuery returns the canonical query string with no leading ?. createQueryUrl returns a URL, and it is the one place outside the runtime that preserves unmanaged keys: managed keys are written first, and everything the model does not declare follows in its original order.

In Server mode the history controls are gone, not merely disabled: there is no stack to move through. Each change represents a new request URL being decoded.

History—

Products

6 matching

  • Edge runtime handbookactive$59
  • Node.js request toolkitactive$39
  • Nuxt deployment guidearchived$19
  • Request URL
  • decode
  • typed result
  • canonical URL

A request is read once. There is no history to move through, so navigation is absent.

Type a query, press Enter.

Typed state

{
  "page": 1,
  "sort": "created_at",
  "status": "all"
}

Canonical URL

Valid
/products

    • An absolute URL is used as given. A string that parses as absolute is not re-based by createQueryUrl; the readers never build a URL at all.
    • A malformed query does not throw. It decodes with issues, exactly as in the browser.
    • createQueryUrl mutates nothing. It copies the base URL before writing to search.
    • Repeated unmanaged keys keep their order and multiplicity.