DEV Community

Cover image for Working with URLs in JavaScript
Megan Lee for LogRocket

Posted on • Originally published at blog.logrocket.com

Working with URLs in JavaScript

Written by Joe Attardi✏️

URLs are a critical piece of any web app. If your app makes requests to an API, it’s important to craft the correct URLs for these requests. The URL API, supported in all modern browsers, provides a way to parse and manipulate URLs. It provides easy access to the various parts of the URL.

Understanding the parts of a URL

Consider the following URL:

https://example.com/api/search?query=foo&sort=asc#results

This URL is made up of the following components:

  • Protocol: https
  • Host: example.com
  • Path name: /api/search
  • Query string: ?query=foo&sort=asc
  • Hash: #results

With modern JavaScript, we can parse URLs and extract these different parts as needed.

Parsing URLs

In older browsers, before the URL API was available, one way that developers parsed URLs was by using an <a> element. This element provides some basic URL parsing. For example, here is a way you could extract the query string from a URL:

function getQueryString(url) {
  const link = document.createElement('a');
  link.href = url;
  return url.search;
}
Enter fullscreen mode Exit fullscreen mode

However, this approach has some disadvantages:

  • It requires a DOM environment, which means it wouldn’t work in an environment such as Node.js
  • It also has no error handling — if an invalid URL is passed to the href attribute, no error is thrown

You could also use a regular expression to parse out the various parts of the URL, but this is tedious and error-prone.

Using the URL API to parse URLs is straightforward. Just pass the URL you want to parse to the URL constructor. If the URL string is valid, you’ll get a URL object back with properties for various parts of the URL:

const url = new URL('https://example.com/api/search?query=foobar');
console.log(url.host); // example.com
console.log(url.pathname); // /api/search
console.log(url.search); // ?query=foobar
Enter fullscreen mode Exit fullscreen mode

Parsing the query string

You can access the query string of a URL in two ways:

  • The search property, which is a string containing the full query string (including the ? character)
  • The searchParams property, which is a URLSearchParams object

If you’re interested in the value of a particular parameter in the query string, you can use its get method to get the parameter by its name:

const url = new URL('https://example.com/api/search?query=foobar&maxResults=10');
console.log(url.searchParams.get('query'); // foobar
console.log(url.searchParams.get('maxResults'); // 10
Enter fullscreen mode Exit fullscreen mode

If there are multiple parameters with the same name, you can use getAll to get an array containing all of the values for that name:

const url = new URL('https://example.com/api/search?tag=tag1&tag=tag2&tag=tag3');
console.log(url.searchParams.getAll('tag')); // ['tag1', 'tag2', 'tag3']
Enter fullscreen mode Exit fullscreen mode

Building query strings

Building a query string by hand can be tricky, especially if any query parameters contain special characters that need to be escaped. For example, if a query parameter needs to contain a & character, you’d need to encode it as %26. To cover these situations, you need to use the encodeURIComponent function:

let queryString = 'foo=bar';
queryString += '&baz=qux';
queryString += '&tag=' + encodeURIComponent('one&two');
console.log(queryString); // foo=bar&baz=qux&tag=one%26two
Enter fullscreen mode Exit fullscreen mode

You can build the query string more safely by using the URLSearchParams object:

const params = new URLSearchParams();
params.append('foo', 'bar');
params.append('baz', 'qux');
params.append('tag', 'one&two');
console.log(params.toString()); // foo=bar&baz=qux&tag=one%26two
Enter fullscreen mode Exit fullscreen mode

Advantages of using URLSearchParams include:

  • You don’t have to worry about the & characters separating the parameters
  • You don’t need to URI-encode the parameter values
  • You don’t need to use string concatenation

Iterating over query parameters

Without a URLSearchParams object, it’s a little tricky to iterate over the parameters in a query string. You would need to split strings several times — first into groups of key/value pairs, then again to split up the key and value:

function listQueryParams(queryString) {
  queryString.split('&').forEach(param => {
    const [key, value] = param.split('=');
    console.log(`${key}: ${value}`);
  });
}
Enter fullscreen mode Exit fullscreen mode

If the parameters could contain encoded characters, you’d also need to decode them:

function listQueryParams(queryString) {
  queryString.split('&').forEach(param => {
    const [key, value] = param.split('=');
    console.log(`${key}: ${decodeURIComponent(value)}`);
  });
}
Enter fullscreen mode Exit fullscreen mode

Instead, you can use URLSearchParams's entries method to iterate over the key/value pairs:

function listQueryParams(queryString) {
  const params = new URLSearchParams(queryString);
  params.entries().forEach(([key, value]) => console.log(`${key}: ${value}`));
}
Enter fullscreen mode Exit fullscreen mode

Building a full URL

Here’s a full example of building a URL with a base URL and some query parameters:

const url = new URL('https://example.com/api/search');
url.searchParams.append('query', 'test');
url.searchParams.append('tag', 'tag1');
url.searchParams.append('tag', 'tag2');

// https://example.com/api/search?query=test&tag=tag1&tag=tag2
console.log(url.toString());
Enter fullscreen mode Exit fullscreen mode

Checking for valid URLs

You might try using a regular expression to validate a URL, but it’s notoriously difficult to craft a regular expression that fully captures a valid URL string.

Instead, you can reach for the URL API. The URL constructor will throw an error if you give it an invalid URL. You can use this to check if a URL is valid:

function isValidURL(url) {
  try {
    new URL(url);
    return true;
  } catch (error) {
    return false;
  }
}
Enter fullscreen mode Exit fullscreen mode

With newer browsers, this is even easier. There’s a newer URL.canParse static method that performs similar validation with a single line of code. Like the isValidURL function above, it takes a potential URL string and returns true or false depending on the validity of the URL string.

Creating relative URLs

The URL API has a powerful mechanism for resolving relative URLs. Normally, the argument to the URL constructor will throw an error if it’s not a full, valid URL. However, you can specify a second argument that serves as a base from which to build a relative URL. If you use the two-argument approach, the first argument doesn’t have to be a valid URL but the second one does.

Let’s look at a simple case first:

new URL('/about', 'https://example.com').href;
Enter fullscreen mode Exit fullscreen mode

The URL constructor takes the base URL of https://example.com and adds the relative path /about, resulting in https://example.com/about.

What about this one:

new URL('profile', 'https://example.com/users').href;
Enter fullscreen mode Exit fullscreen mode

You might expect this to be https://example.com/users/profile, but it actually comes out to https://example.com/profile. This behaves just like a relative link; it takes the parent path segment, which is the root of example.com, and then adds profile.

Let’s look at one more example of using a relative URL. You can also use .. to go back up the path hierarchy:

new URL('../profile', 'https://example.com/users/123').href;
Enter fullscreen mode Exit fullscreen mode

This one comes out to https://example.com/profile. Remember that relative URLs start at the parent path segment. Then, this one has .. in it, which goes up one more path segment.

If you call the URL constructor with a relative URL and specify an invalid or incomplete URL for the base URL, you’ll get an error. You’ll also get an error if you use a relative URL without a full base URL:

new URL('../profile', '/about'); // error!
new URL('../profile'); // error
Enter fullscreen mode Exit fullscreen mode

Working with the window.location object

You might be familiar with the window.location object, which represents the current page’s URL. This object also has properties such as href and pathname, so you might think it’s a URL object. This is a different object, a Location, which has some properties in common with URL, but is also missing some (such as the searchParams property).

Even though it’s not a URL object, you can still use window.location to construct new URL objects. You can pass window.location to the URL constructor to create a new full-blown URL with searchParams and all, based on the current URL, or you can even use it as the base URL when constructing relative URLs:

new URL('/profile', window.location).href;
Enter fullscreen mode Exit fullscreen mode

Matching patterns within a URL using URLPattern

Using a URL makes it easy to get the path from a URL. For example, in the URL https://example.com/api/users/123/profile, the path name is /api/users/123/profile. What if we wanted to get just the user ID, 123, from this URL?

As we discussed earlier, it can be difficult to create proper regular expressions to validate and extract parts of a URL.

It’s not available in all browsers just yet, but you can use the URLPattern API to match and extract parts of the URL, matching patterns you specify. This can be particularly useful for things like client-side routing in a single-page application (SPA).

Using the user profile URL as an example, let’s create a URLPattern to get the user ID. We can use a leading : character to denote a named placeholder, which can be used later to match that part of the URL:

const pattern = new URLPattern('https://example.com/api/users/:userId/profile');
const matcher = pattern.exec('https://example.com/api/users/123/profile');
console.log(matcher.pathname.groups.userId); // 123
Enter fullscreen mode Exit fullscreen mode

When you call exec on a URLPattern, it needs a valid URL. It returns a matcher object that contains properties for each of the URL’s parts (protocol, host, pathname, etc.). Each of these properties also has a groups property, which maps placeholder names like :userId to their values within the URL.

If you’re only concerned about matching one part of the URL, such as the path name as we’ve done here, you can specify wildcards in the URL pattern as well. Or, instead of a URL string, you can pass an object containing the parts of the URL you’re interested in matching:

new URLPattern('https://*/api/users/:userId/profile');
new URLPattern({ pathname: '/api/users/:userId/profile' });
Enter fullscreen mode Exit fullscreen mode

The URLPattern API is still not available in all browsers. At the time of writing, it’s not yet supported in Firefox or Safari. You can see the latest browser support information at CanIUse.com.

Summary

The URL API is a versatile interface for constructing, validating, and manipulating URLs in JavaScript. It’s safer and less error-prone to use than manual parsing or regular expressions. By using a URLSearchParams object, you can build a query string without worrying about string concatenation or encoding special characters.

The URLPattern API takes this a step further, supporting wildcards and named placeholders, so you can slice and dice URLs to meet your app’s needs! Further reading:


Get set up with LogRocket's modern error tracking in minutes:

  1. Visit https://logrocket.com/signup/ to get an app ID.
  2. Install LogRocket via NPM or script tag. LogRocket.init() must be called client-side, not server-side.

NPM:

$ npm i --save logrocket 

// Code:

import LogRocket from 'logrocket'; 
LogRocket.init('app/id');
Enter fullscreen mode Exit fullscreen mode

Script Tag:

Add to your HTML:

<script src="https://cdn.lr-ingest.com/LogRocket.min.js"></script>
<script>window.LogRocket && window.LogRocket.init('app/id');</script>
Enter fullscreen mode Exit fullscreen mode

3.(Optional) Install plugins for deeper integrations with your stack:

  • Redux middleware
  • ngrx middleware
  • Vuex plugin

Get started now

Top comments (1)

Collapse
 
prodevstaff profile image
ProDev. Staff