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

Encoding and special characters

URLs can only contain certain characters. Letters, numbers, and a handful of symbols are fine. But spaces, ampersands, question marks, and many other characters have special meaning or simply aren’t allowed. So what happens when your data contains them?

Let’s find out by breaking something.

The problem

Imagine you want to search for shoes & boots. If you just paste that into a URL as-is:

https://shop.com/search?query=shoes & boots

This breaks. The browser sees:

  • query=shoes as a parameter with the value "shoes"
  • A space, which is invalid in URLs
  • & boots as a new parameter name called boots with no value

The & has special meaning in URLs (it separates parameters), so you can’t use it literally inside a value. The space is simply not allowed. Your single search term just got torn apart into two broken pieces.

Percent-encoding

The solution is percent-encoding (also called URL encoding). Special characters are replaced with a % followed by their two-digit hexadecimal code:

CharacterEncoded as
space%20 or +
&%26
=%3D
?%3F
#%23
/%2F
+%2B

So shoes & boots becomes shoes+%26+boots in a query string. The space becomes + and the & becomes %26. Now the browser can see that the entire thing is a single value, not two separate parameters.

Automatic encoding with URLSearchParams

The best part about URLSearchParams? It handles encoding automatically. You never have to think about it:

Code along
const params = new URLSearchParams();
params.set("query", "shoes & boots");
params.set("filter", "price < 100");

console.log(params.toString());
// "query=shoes+%26+boots&filter=price+%3C+100"

The & in “shoes & boots” became %26. The < in “price < 100” became %3C. The spaces became +. URLSearchParams did all of this without you asking.

And when you read the value back, it’s automatically decoded:

params.get("query"); // "shoes & boots"   ← clean, original value!
params.get("filter"); // "price < 100"

You never see the encoded version when using .get(). Encoding only exists in the serialized string form (.toString()). When you read the data back, you get the original, human-readable version.

Automatic encoding with URL

The URL object also handles encoding in pathnames:

const url = new URL("https://example.com");
url.pathname = "/products/running shoes";

console.log(url.href);
// "https://example.com/products/running%20shoes"

The space in “running shoes” was automatically encoded as %20. You assigned a readable string, and the URL object took care of making it valid.

Manual encoding functions

Sometimes you need manual control over encoding. JavaScript provides two pairs of functions for this.

encodeURIComponent() / decodeURIComponent()

Use these for encoding individual values, like a single query parameter value or a single path segment:

encodeURIComponent("hello world & more");
// "hello%20world%20%26%20more"

decodeURIComponent("hello%20world%20%26%20more");
// "hello world & more"

encodeURIComponent encodes everything that isn’t a basic letter, number, or one of a few safe characters (-, _, ., ~). It’s aggressive, and that’s the point. When you’re encoding a single value, you want to make sure nothing in it could be misinterpreted as URL structure.

encodeURI() / decodeURI()

Use these for encoding full URLs. They preserve characters that have structural meaning in URLs, like :, /, ?, #, and &:

encodeURI("https://example.com/path with spaces?q=hello world");
// "https://example.com/path%20with%20spaces?q=hello%20world"

The spaces got encoded, but the ://, /, and ? were left alone because they’re part of the URL structure.

The key difference

encodeURIComponent("a=1&b=2"); // "a%3D1%26b%3D2"  ← encodes = and &
encodeURI("a=1&b=2"); // "a=1&b=2"         ← keeps = and &

encodeURIComponent encodes everything special. encodeURI preserves URL structure characters. Use the wrong one and you’ll either break URL structure or fail to encode dangerous characters.

Rule of thumb

SituationWhat to use
Building any URLUse the URL class (best option)
Working with query paramsUse URLSearchParams (handles encoding for you)
Encoding a single value manuallyUse encodeURIComponent()
Encoding a full URL stringUse encodeURI()

In practice, if you’re using URL and URLSearchParams, you almost never need the manual functions. They exist for edge cases where you’re building URL strings by hand.

The + vs %20 confusion

In query strings, spaces can be encoded as either + or %20. Both are valid:

?query=hello+world     ← valid
?query=hello%20world   ← also valid

URLSearchParams uses + for spaces in query strings. The URL pathname uses %20. This follows the web standards. You don’t need to choose between them. The tools pick the right one based on context.

This is one reason why @hectoday/http has its own custom query parser (which we’ll look at next). It needs to handle + as spaces when decoding query values:

// From @hectoday/http source:
const normalized = value.replace(/\+/g, " ");

That one line converts every + back into a space before decoding the rest. It’s a small detail, but getting it wrong means your users’ search queries would contain literal + symbols instead of spaces.

With encoding covered, we’re ready to look inside @hectoday/http itself. In the next lesson, you’ll see the actual query parsing code the framework uses and understand exactly why it works the way it does.

What does new URLSearchParams({ q: 'a & b' }).get('q') return?

← URL and SearchParams together How @hectoday/http parses queries →

© 2026 hectoday. All rights reserved.