Skip to content
QueryWeave

Quick start

Steps 1 to 4 use only @queryweave/core and run anywhere. Step 5 connects the model to the place your query lives.

  1. products.ts
    import { defineQueryModel, param } from "@queryweave/core";
    export const products = defineQueryModel({
    search: param.text().optional(),
    page: param.integer({ min: 1 }).default(1),
    sort: param.choice(["name", "created_at", "price"]).default("created_at"),
    status: param.choice(["all", "active", "archived"]).default("all"),
    });

    search is optional: when its key is absent, the decoded value is undefined. The other three have defaults, so they always decode to a concrete value even when the key is missing from the URL.

  2. const result = products.decode("?search=vue&page=2");
    result.ok; // true for this input
    if (result.ok) {
    result.value;
    // {
    // search: "vue", — from the URL
    // page: 2, — from the URL, typed as number
    // sort: "created_at", — not in the URL; the default fills it in
    // status: "all", — same
    // }
    }

    decode returns complete typed state: every key in the model, with defaults used when a key is missing from the URL. It accepts a query string, an iterable of entries (including URLSearchParams), or a plain object, and it never throws.

  3. const result = products.decode("?page=abc");
    result.ok; // true — the invalid page recovered to its default
    if (result.ok) {
    result.value.page; // 1
    result.value.sort; // "created_at"
    }
    result.issues[0]?.code; // "invalid"
    result.issues[0]?.key; // "page"

    An invalid value produces an issue and a recovery. page falls back to its default rather than vanishing, so the page still renders while the problem stays reportable.

  4. products.encode({ search: "vue", page: 1, sort: "created_at", status: "all" });
    // [["search", "vue"]]

    page, sort, and status are omitted because each equals its declared default. That makes the output canonical: ?page=1 and a URL with no page decode to the same state, and that state encodes back without page.

  5. Pick where your query lives. In the browser, Vue Router, or Nuxt, a runtime reads the URL, writes it, and reports changes. On a server or in Node.js, you read the request’s query once.

    Where are you using QueryWeave?

    Browser

    History API synchronization with push, replace, and popstate.

    PackagePackage: @queryweave/browser
    Install
    Terminal window
    pnpm add @queryweave/core @queryweave/browser
    import { createBrowserAdapter } from "@queryweave/browser";
    import { createQueryRuntime } from "@queryweave/core";
    const runtime = createQueryRuntime({
    model: products,
    adapter: createBrowserAdapter(),
    });
    await runtime.update({ search: "vue" });
    Read the Browser guide

    Server

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

    PackagePackage: @queryweave/server
    Install
    Terminal window
    pnpm add @queryweave/core @queryweave/server
    import { readRequestQuery } from "@queryweave/server";
    export function handle(request: Request) {
    const result = readRequestQuery(request, products);
    return result.ok ? result.value : { ...products.defaults(), ...result.partial };
    }
    Read the Server guide

    Node.js

    Node request primitives bridged into the server helpers.

    PackagePackage: @queryweave/node
    Install
    Terminal window
    pnpm add @queryweave/core @queryweave/server @queryweave/node
    import { readNodeQuery } from "@queryweave/node";
    createServer((request, response) => {
    const result = readNodeQuery(request, products);
    response.end(JSON.stringify(result.ok ? result.value : result.partial));
    });
    Read the Node.js guide

    Vue

    Readonly reactive values and explicit operations, bound to one model.

    PackagePackage: @queryweave/vue
    Install
    Terminal window
    pnpm add @queryweave/core @queryweave/vue
    import { useQueryModel } from "@queryweave/vue";
    const filters = useQueryModel(products);
    filters.values.page; // readonly reactive state
    await filters.update({ page: 2 });
    Read the Vue guide

    Vue Router

    A router-backed adapter that keeps path and hash intact.

    PackagePackage: @queryweave/vue-router
    Install
    Terminal window
    pnpm add @queryweave/core @queryweave/vue-router
    import { createVueRouterAdapter } from "@queryweave/vue-router";
    import { provideQueryAdapter } from "@queryweave/vue";
    provideQueryAdapter(createVueRouterAdapter(router));
    Read the Vue Router guide

    Nuxt

    A module plus a request-scoped runtime plugin for server rendering.

    PackagePackage: @queryweave/nuxt
    Install
    Terminal window
    pnpm add @queryweave/core @queryweave/vue @queryweave/nuxt
    nuxt.config.ts
    export default defineNuxtConfig({
    modules: ["@queryweave/nuxt"],
    });
    Read the Nuxt guide

The simulator below is driven by the model from step 1, createQueryRuntime from @queryweave/core, and the memory adapter from @queryweave/testing. Switch Navigation between push and replace and watch the history counter; switch Adapter to Server and the history controls disappear, because a request has none.

Typing in the search box updates the URL through a real runtime transaction: search is set, page resets to 1, and defaults are omitted from the encoded query.

History1 / 1

Products

6 matching

  • Edge runtime handbookactive$59
  • Node.js request toolkitactive$39
  • Nuxt deployment guidearchived$19
  • history.pushState
  • history.replaceState
  • popstate

The adapter writes through the History API and re-reads on popstate.

Type a query, press Enter.

Typed state

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

Canonical URL

Valid
/products

    • Query models — composition, defaults, and model-level refinement.
    • The query runtime — transitions, subscriptions, and unmanaged keys.
    • Adapters — where a query is actually stored.