typescript 32 lines · 8 steps

Building nested query strings recursively

A recursive encoder flattens arrays and nested objects into a URL-safe query string.

Explained by highlit
1type QueryValue = string | number | boolean | null | undefined;
2type QueryInput = QueryValue | QueryValue[] | { [key: string]: QueryInput };
3 
4function buildQueryString(params: Record<string, QueryInput>): string {
5 const pairs: string[] = [];
6 
7 const encode = (key: string, value: QueryInput): void => {
8 if (value === null || value === undefined) return;
9 
10 if (Array.isArray(value)) {
11 for (const item of value) {
12 encode(`${key}[]`, item);
13 }
14 return;
15 }
16 
17 if (typeof value === "object") {
18 for (const [childKey, childValue] of Object.entries(value)) {
19 encode(`${key}[${childKey}]`, childValue);
20 }
21 return;
22 }
23 
24 pairs.push(`${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`);
25 };
26 
27 for (const [key, value] of Object.entries(params)) {
28 encode(key, value);
29 }
30 
31 return pairs.length ? `?${pairs.join("&")}` : "";
32}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1A recursive type mirrors a recursive function — the type describes exactly what the encoder can flatten.
  2. 2Recursion turns nested arrays and objects into flat key paths without special-casing depth.
  3. 3Deferring encodeURIComponent to leaf values keeps bracket syntax in keys readable while still escaping user data.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building nested query strings recursively — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code