Skip to content
Runtime
Framework

Quick start

Five steps, none of which mention a framework until the last one.

  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. Canonical output is the shortest encoding that decodes back to the same state.

  5. Everything above works without a browser or framework. A runtime connects the model to a place that stores and navigates the query.

    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

    • One description of the query, usable on both sides of a request.
    • Types that follow from the model rather than being written twice.
    • Canonical URLs, so equal states produce equal links.
    • Issues you can render, rather than exceptions you must catch.
    • Query models — composition, defaults, and model-level refinement.
    • The query runtime — transitions, subscriptions, and unmanaged keys.
    • Adapters — where a query is actually stored.