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.
Consider the following URL:
https://example.com/api/search?query=foo&sort=asc#results
This URL is made up of the following components:
https
example.com
/api/search
?query=foo&sort=asc
#results
With modern JavaScript, we can parse URLs and extract these different parts as needed.
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; }
However, this approach has some disadvantages:
href
attribute, no error is thrownYou 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
You can access the query string of a URL
in two ways:
search
property, which is a string containing the full query string (including the ?
character)searchParams
property, which is a URLSearchParams
objectIf 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
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']
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
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
Advantages of using URLSearchParams
include:
&
characters separating the parametersWithout 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}`); }); }
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)}`); }); }
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}`)); }
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());
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; } }
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.
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;
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;
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;
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
window.location
objectYou 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;
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
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' });
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.
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:
Hey there, want to help make our blog better?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowIn this guide, explore lazy loading and error loading as two techniques for fetching data in React apps.
Deno is a popular JavaScript runtime, and it recently launched version 2.0 with several new features, bug fixes, and improvements […]
Generate OpenAPI API clients in Angular to speed up frontend development, reduce errors, and ensure consistency with this hands-on guide.
Making carousels can be time-consuming, but it doesn’t have to be. Learn how to use React Snap Carousel to simplify the process.