BBizKit

query.canonicalParams

v0.1.0 latest

Produce a canonical query string sorted by key then by value for deterministic output per RFC 3986.

query-stringcanonicalsortingsigningcache-keydeterministic

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:

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.

Referenced Standards