Recipe 3.6

Matching URLs to Patterns

This demo shows how to match patterns in URLs using the URLPattern API. For each pattern input, you can enter an exact string to match or include wildcards and named groups.

Enter a URL in the last input and click “Check URL” to parse the URL and match against your pattern.

Example patterns

Try some of these patterns for the hostname or pathname:

  • Exact match: /api/users
  • Wildcards: /api/users/*
  • Named groups: /api/users/:userId/profile
  • Mix and match: /api/groups/:groupId/*/users/:userId

Demo

URL patterns

Code

JavaScript
const form = document.querySelector('form');
const resultsContainer = document.querySelector('#results');

form.addEventListener('submit', event => {
  event.preventDefault();

  const data = new FormData(event.target);

  const patterns = {};
  if (data.get('hostname')) {
    patterns.hostname = data.get('hostname');
  }

  if (data.get('pathname')) {
    patterns.pathname = data.get('pathname');
  }

  const pattern = new window.URLPattern(patterns);

  // Attempt to match our pattern against our URL.
  const matcher = pattern.exec(data.get('url'));

  // `matcher` is null if the URL doesn't match.
  if (!matcher) {
    console.log('URL does not match pattern')
    const results = document.querySelector('#result-template-noMatch').content.cloneNode(true).firstElementChild;
    results.querySelector('.url').textContent = data.get('url');
    resultsContainer.replaceChildren(results);
  } else {
    console.log('URL matches pattern');
    console.log('Matcher data:', matcher);

    const results = document.querySelector('#result-template-match').content.cloneNode(true).firstElementChild;
    results.querySelector('.url').textContent = data.get('url');

    const details = results.querySelector('.details');

    // The matcher has multiple groups for each placeholder, we'll render
    // each separately.
    details.appendChild(renderMatchGroup(matcher, 'hostname'));
    details.appendChild(renderMatchGroup(matcher, 'pathname'));

    resultsContainer.replaceChildren(results);
  }
});

function renderMatchGroup(matcher, key) {
  const groupList = document.createElement('ul');
  const group = document.createElement('li');
  group.innerHTML = `<strong>${key}</strong>: <span class="font-monospace">${matcher[key].input}</span>`;
  groupList.appendChild(group);

  const matchGroups = document.createElement('ul');

  for (const groupKey of Object.keys(matcher[key].groups)) {
    if (groupKey === '0') {
      continue;
    }
    
    
    const item = document.createElement('li');
    item.innerHTML = `<strong>${groupKey}</strong>: <code>${matcher[key].groups[groupKey]}`;
    matchGroups.appendChild(item);
  }

  groupList.appendChild(matchGroups);

  return groupList;
}
HTML
<!-- Template to render a URL that doesn't match -->
<template id="result-template-noMatch">
  <div class="alert alert-danger mt-4">
    <h4 class="url mb-2"></h4>
    <div class="my-2">URL does not match pattern</div>
  </div>
</template>

<!-- Template to render a matching URL -->
<template id="result-template-match">
  <div class="alert alert-success mt-4">
    <h4 class="url text-2xl mb-2"></h4>
    <div class="my-2">URL matches pattern</div>
    <div class="my-2 fw-bold">Match details:</div>
    <ul class="details">
    </ul>
  </div>
</template>


<form>
  <fieldset class="mb-3">
    <legend>URL patterns</legend>
    <div class="mb-3">
      <label class="form-label" for="url">Host name</label>
      <input class="form-control" id="hostname" type="text" name="hostname">
    </div>
    <div class="mb-3">
      <label class="form-label" for="url">Path name</label>
      <input class="form-control" id="pathname" type="text" name="pathname" value="/api/users/:userId">
    </div>
  </fieldset>
  <div class="mb-3">
    <label class="form-label" for="url">URL to match</label>
    <input class="form-control" id="url" type="url" required name="url" value="https://example.com/api/users/sysadmin">
  </div>
  <button class="btn btn-primary">Check URL</button>
</form>

<div id="results"></div>
Web API Cookbook
Joe Attardi