Why QueryWeave
The same query is read in many places
Section titled “The same query is read in many places”A server renders the first response from it. The client reads it again after hydration. The user shares it as a link. A test asserts the result. A helper tied to one framework’s component lifecycle can only cover one of those reads.
QueryWeave defines the query as a model and syncs it through environment-specific adapters. The model is portable; the adapter changes with the environment.
Decoding and encoding stay together
Section titled “Decoding and encoding stay together”Reading and writing a URL are one problem. When a parser and a serializer are configured separately, they drift: the parser accepts values the serializer cannot produce, and round-tripping stops being stable.
In QueryWeave, each parameter has one codec for both directions. That is why there is no
createParser, no separate serializer, and no way to configure decode and encode independently.
Defaults are part of the meaning, not of the reader
Section titled “Defaults are part of the meaning, not of the reader”If a default lives in the component that reads the query, every other reader must repeat it, and
the URL cannot be canonical — ?page=1 and an absent page mean the same thing but produce
different strings.
Declaring the default on the parameter fixes both problems at once. The default participates in decoding and in encoding, which is what makes omission possible.
Invalid input is normal
Section titled “Invalid input is normal”Query strings arrive from links, bookmarks, crawlers, and typos. Treating bad input as an exception
forces every call site into a try/catch that usually swallows the cause.
QueryWeave returns a result. A failed decode still carries partial values and a list of issues
with stable codes, so an application can render something useful and report what went wrong.
What QueryWeave is not
Section titled “What QueryWeave is not”QueryWeave is not a port, a wrapper, or a rename of another library.
- It is not a framework utility.
@queryweave/corehas no framework dependency, and a repository check enforces that — not convention alone. - It is not a query-string helper. Parsing a string is the smallest part; the model, the issues, the canonical form, and the transition semantics are the substance.
- It is not a collection of composables. Vue and Nuxt are integrations built on the same runtime a plain TypeScript application uses.
The public API is deliberately model-first. Hook-shaped identities, per-key hooks, tuple setters,
and parseAs*-style constructors are rejected by name in
ADR 0001,
and a test in the repository fails the build if one ever appears.
What that costs
Section titled “What that costs”Model-first is not free, and the trade-offs are real:
- More to write up front. A model must be declared before anything can read a single value.
For an application with one
?tab=parameter, that is more ceremony than it is worth. - No per-key ergonomics. There is no
useTab(). You readbinding.values.taband call an explicit operation to change it. - Transitions are immediate. Without scheduling, a fast-changing input such as a search field writes on every keystroke unless you debounce it yourself. Scheduling is planned, not present.
URL state as a domain walks through the concepts in order.