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

URL properties deep dive

You’ve seen how to create a URL object and read a few of its properties. But the URL object isn’t just for reading. Most of its properties are read-write, meaning you can change any part of a URL by assigning a new value. Let’s go through all of them and pay special attention to the ones that trip people up.

Every property at a glance

Let’s start with a URL that uses every part, including some you don’t see very often:

Code along
const url = new URL("https://user:[email protected]:8080/path?q=1#top");

This URL contains a username (user) and password (pass) embedded right into the string. You won’t see this often in modern web development, but it’s part of the URL specification.

Here are all the properties you can access on this object:

url.href; // "https://user:[email protected]:8080/path?q=1#top"
url.protocol; // "https:"
url.username; // "user"
url.password; // "pass"
url.host; // "example.com:8080"      ← hostname + port
url.hostname; // "example.com"           ← just the domain
url.port; // "8080"
url.pathname; // "/path"
url.search; // "?q=1"
url.hash; // "#top"
url.origin; // "https://example.com:8080"   ← READ-ONLY!
url.searchParams; // URLSearchParams object        ← (next lesson!)

That’s a lot of properties. You won’t use all of them equally. pathname, search, hostname, and searchParams are the ones you’ll reach for most often in backend code. But knowing the full list helps when you run into edge cases.

Modifying URLs safely

Here’s the part that makes URL objects so useful. You can assign to most of these properties and the URL updates itself correctly:

const url = new URL("https://example.com/old-page");

url.pathname = "/new-page";
console.log(url.href);
// "https://example.com/new-page"

url.protocol = "http:";
console.log(url.href);
// "http://example.com/new-page"

url.hash = "section-3";
console.log(url.href);
// "http://example.com/new-page#section-3"

url.port = "3000";
console.log(url.href);
// "http://example.com:3000/new-page#section-3"

Each time we assign to a property, the URL object keeps everything consistent. You don’t have to worry about accidentally breaking the URL format. It handles the colons, slashes, and question marks for you.

What do you think would happen if you set url.pathname = "new-page" without the leading slash? Try it. The URL object adds the slash for you automatically. It’s designed to keep things valid.

The confusing trio: host vs hostname vs origin

These three are easy to mix up, and they come up constantly. Here’s the definitive guide:

const url = new URL("https://example.com:8080/page");

url.hostname; // "example.com"             ← just the domain name
url.host; // "example.com:8080"        ← domain name + port
url.origin; // "https://example.com:8080" ← protocol + domain + port

Think of it as layers, where each one adds more:

hostname  →  example.com
host      →  example.com:8080          (hostname + port)
origin    →  https://example.com:8080  (protocol + host)

hostname is the smallest piece: just the domain. host adds the port. origin adds the protocol on top of that.

⚠ Warning

origin is read-only. You cannot set it directly. Writing url.origin = "http://other.com" does nothing (no error, just silently ignored). To change the origin, modify protocol, hostname, or port individually.

Default ports are hidden

When the port is the default for the protocol (443 for HTTPS, 80 for HTTP), something subtle happens. The port property returns an empty string:

const url = new URL("https://example.com");
url.port; // ""               ← port 443 is the default, so it's hidden
url.host; // "example.com"    ← no ":443" shown
url.origin; // "https://example.com"

const url2 = new URL("https://example.com:9999");
url2.port; // "9999"               ← non-default, so it shows
url2.host; // "example.com:9999"

This catches people off guard. If you check url.port expecting to see "443", you’ll get an empty string instead. The port is still 443, but the URL object hides it because it’s implied by the protocol.

Hostname is always lowercased

The URL specification says domain names are case-insensitive. The URL constructor normalizes this for you:

const url = new URL("https://EXAMPLE.COM/Path");

url.hostname; // "example.com"  ← lowercased automatically
url.pathname; // "/Path"        ← pathname keeps its case!

This is important to notice: hostname is lowercased, but pathname is NOT. The paths /Users and /users are considered different resources on a server. The URL object respects that distinction.

search vs searchParams

These two are related but different, and understanding the distinction now will save you confusion later:

const url = new URL("https://example.com/page?color=red&size=10");

url.search; // "?color=red&size=10"
url.searchParams; // URLSearchParams { 'color' => 'red', 'size' => '10' }

search is the raw query string as a plain string, starting with ?. It’s just text.

searchParams is a URLSearchParams object with helpful methods like .get(), .set(), and .delete(). It’s a structured, interactive representation of the same data.

You can use either one depending on what you need. But searchParams is almost always the better choice because it gives you methods to read and modify individual parameters without parsing the string yourself.

We’ll explore searchParams deeply in the next lesson.

Which URL property is read-only (you can't change it by assigning a value)?

← The URL constructor URLSearchParams basics →

© 2026 hectoday. All rights reserved.