hectoday
DocsCoursesChangelog GitHub
DocsCoursesChangelog GitHub

Access Required

Enter your access code to view courses.

Invalid code

← All courses URL mastery with @hectoday/http

Understanding URLs

  • What is a URL?
  • The URL constructor
  • URL properties deep dive

URLSearchParams

  • URLSearchParams basics
  • Modifying search params
  • Iterating over params
  • URL and SearchParams together

Encoding

  • Encoding and special characters

Inside @hectoday/http

  • How @hectoday/http parses queries
  • Routing and URL patterns
  • The Request object and URLs
  • Building API URLs
  • Input validation and query schemas

Putting it all together

  • Capstone: bookmarks API

Iterating over params

So far, you’ve been reading params by name with get() and getAll(). But sometimes you don’t know all the key names ahead of time. Maybe a user sends a URL with a bunch of filter parameters and you want to process all of them. For that, you need to loop through the URLSearchParams.

Good news: URLSearchParams is iterable, which means you can loop over it just like an array.

for...of: the most common way

const params = new URLSearchParams("name=Alice&role=admin&theme=dark");

for (const [key, value] of params) {
  console.log(`${key} → ${value}`);
}
// name → Alice
// role → admin
// theme → dark

Each iteration gives you a [key, value] pair, which is an array of two strings. We use destructuring (const [key, value]) to grab both parts cleanly in one line. Without destructuring, you’d write const pair = ... and then access pair[0] and pair[1], which is less readable.

This is the approach you’ll use most of the time.

forEach(): the callback way

params.forEach((value, key) => {
  console.log(`${key}: ${value}`);
});
// name: Alice
// role: admin
// theme: dark
⚠ Warning

The parameter order in forEach is (value, key), backwards from what you might expect. Most people expect (key, value), but URLSearchParams follows the same pattern as Map.forEach(). Getting this wrong is a very common bug.

Let’s look at what that mistake looks like in practice:

// Wrong — the arguments are swapped
params.forEach((key, value) => {
  console.log(key); // This is actually the VALUE, not the key!
});

// Correct
params.forEach((value, key) => {
  console.log(key, value);
});

The code runs without errors either way. JavaScript doesn’t care what you name your variables. But if you name them (key, value), you’ll be printing values when you think you’re printing keys. It’s a silent bug. The for...of approach doesn’t have this problem because [key, value] destructuring matches the actual pair order.

keys(), values(), and entries()

These methods return iterators that you can spread into arrays:

const params = new URLSearchParams("a=1&b=2&c=3");

[...params.keys()]; // ["a", "b", "c"]
[...params.values()]; // ["1", "2", "3"]
[...params.entries()]; // [["a", "1"], ["b", "2"], ["c", "3"]]

keys() gives you just the parameter names. values() gives you just the values. entries() gives you the [key, value] pairs, which is the same thing you get when you iterate over params directly with for...of.

The spread syntax [...] converts the iterator into a real array, so you can use array methods like .map(), .filter(), or .includes() on the result.

Converting to a plain object

Here’s a very handy trick. You can use Object.fromEntries() to turn URLSearchParams into a regular JavaScript object:

const params = new URLSearchParams("name=Alice&age=30&role=admin");

const obj = Object.fromEntries(params);
// { name: "Alice", age: "30", role: "admin" }

Object.fromEntries() takes anything that produces [key, value] pairs and turns it into a plain object. Since URLSearchParams is iterable and yields [key, value] pairs, it works perfectly.

This is great when you want to pass search params to a function that expects a plain object, or when you want to destructure the values.

⚠ Warning

Object.fromEntries() can only keep one value per key. If you have duplicates, only the last one survives:

const params = new URLSearchParams("x=1&x=2&x=3");
Object.fromEntries(params);
// { x: "3" }   ← only the last value!

If you need to preserve duplicate keys, use getAll() or iterate manually.

What do you think would happen if you convert "tag=js&tag=css&tag=html" to an object? You’d lose "js" and "css" entirely. Only "html" (the last value) would remain. This is a common trap when working with multi-value query strings.

Practical example: building a filter description

Let’s put iteration to work. Imagine you’re building an e-commerce API and want to log which filters the user applied:

const params = new URLSearchParams("color=red&size=M&brand=Nike&brand=Adidas");

const filters = [];
for (const [key, value] of params) {
  filters.push(`${key}: ${value}`);
}

console.log("Active filters: " + filters.join(", "));
// "Active filters: color: red, size: M, brand: Nike, brand: Adidas"

We create an empty filters array, then loop through every entry in params. For each one, we push a formatted string like "color: red" into the array. Finally, we join them all with commas into a single readable string.

Notice that brand appears twice because the user selected two brands. The iteration gives you every entry, including duplicates.

In the next lesson, you’ll see how URLSearchParams and the URL object work together through a live connection that keeps them in sync automatically.

In params.forEach((val, key) => ...), which argument comes first?

← Modifying search params URL and SearchParams together →

© 2026 hectoday. All rights reserved.