query.canonicalParams
v0.1.0 latestProduce a canonical query string sorted by key then by value for deterministic output per RFC 3986.
Produce a canonical query string sorted by key then by value for deterministic output per RFC 3986.
Signature
function canonicalParams(params: Record<string, unknown> | [string, string][], opt?: CanonicalParamsOptions): string
Type Definitions:
CanonicalParamsOptions— interface
Problem
Generating signatures, cache keys, or ETags from query parameters requires deterministic serialization — the same parameters must always produce the same string regardless of insertion order. JavaScript object key order is not guaranteed across environments.
How It Works
Sorts by key name (byte-order), then by value for same-name keys. Uses RFC 3986 encoding by default. Accepts both Record and [key, value] tuple arrays.
Boundaries
- Default encoding is rfc3986 (space → %20), unlike stringify which defaults to WHATWG.
- Accepts both Record<string, unknown> and [string, string][] tuple arrays.
- Array values within Records are flattened to individual [key, value] pairs before sorting.
- Output does not include a leading ?.
Replaces
Common boilerplate this function replaces:
Object.keys(params).sort().map(k => encodeURIComponent(k) + '=' + encodeURIComponent(params[k])).join('&')
Examples
query.canonicalParams({ z: "1", a: "2" }); // "a=2&z=1"
query.canonicalParams({ b: ["3","1","2"], a: "x" }); // "a=x&b=1&b=2&b=3"
query.canonicalParams([["b","2"],["a","1"]]); // "a=1&b=2"
Standards
Caveats
- Sort is byte-order (not localeCompare), ensuring deterministic output across all platforms (NHP-04).
- Default encoding differs from query.stringify — canonicalParams defaults to rfc3986.
FAQ
How to generate a canonical query string for signing?
query.canonicalParams({ b: '2', a: '1' }) returns 'a=1&b=2'. Deterministic output for signatures and cache keys.
Does canonicalParams sort array values?
Yes. query.canonicalParams({ a: ['3','1','2'] }) returns 'a=1&a=2&a=3'.
Can I pass tuple arrays to canonicalParams?
Yes. query.canonicalParams([['b','2'],['a','1']]) returns 'a=1&b=2'.
Why does canonicalParams default to RFC 3986 encoding?
RFC 3986 encoding (%20 for spaces) is the standard for URI canonicalization and signing.
Is the sort locale-dependent?
No. Byte-order sort is used to ensure deterministic output across all platforms.