URLPattern API 実践:ブラウザネイティブルーティングマッチングとパターン解析

技术架构(更新: 2026年6月16日)

URLPattern API 概要

URLPatternは、Expressルーティング構文にインスパイアされたブラウザネイティブのURLパターンマッチングインターフェースです。プロトコル、ホスト名、ポート、パス名、検索パラメータ、ハッシュの独立したパターンマッチングをサポートし、より強力な機能を提供します。

const pattern = new URLPattern({ pathname: '/api/users/:id' });
const result = pattern.exec('https://example.com/api/users/42');
console.log(result.pathname.groups.id);

パターン構文詳解

基礎構文:ワイルドカードと名前付きグループ

構文 意味 マッチ
* ワイルドカード、任意の文字列にマッチ /api/* /api/anything
:name 名前付きキャプチャグループ /users/:id /users/42{id: "42"}
:name+ 複数セグメントにマッチする名前付きグループ /files/:path+ /files/a/b/c{path: "a/b/c"}
(.*) 正規表現グループ /old/(.*) /old/page{0: "page"}

正規表現制約付き名前付きグループ

const pattern = new URLPattern({
  pathname: '/products/:id(\\d+)'
});

pattern.test('https://shop.com/products/123');
pattern.test('https://shop.com/products/abc');

完全URLコンポーネントパターン

const apiPattern = new URLPattern({
  protocol: 'https',
  hostname: ':sub.example.com',
  pathname: '/v:version/:resource/:id?',
  search: 'sort=:sort&*'
});

const match = apiPattern.exec({
  protocol: 'https',
  hostname: 'api.example.com',
  pathname: '/v2/users/42',
  search: 'sort=name&page=1'
});

if (match) {
  console.log(match.hostname.groups.sub);
  console.log(match.pathname.groups.version);
  console.log(match.pathname.groups.resource);
  console.log(match.pathname.groups.id);
  console.log(match.search.groups.sort);
}

Service Worker ルーティングインターセプション

URLPatternとService Workerの組み合わせは、ブラウザネイティブルーティングレイヤーの中核シナリオです。

URLPatternベースのSWルートディスパッチ

self.addEventListener('fetch', (event) => {
  const routes = [
    { pattern: new URLPattern({ pathname: '/api/*' }), handler: handleApi },
    { pattern: new URLPattern({ pathname: '/static/*' }), handler: handleStatic },
    { pattern: new URLPattern({ pathname: '/pages/:slug' }), handler: handlePage },
  ];

  for (const { pattern, handler } of routes) {
    const result = pattern.exec(event.request.url);
    if (result) {
      event.respondWith(handler(event, result));
      return;
    }
  }
});

async function handleApi(event, match) {
  const cache = await caches.open('api-cache');
  const cached = await cache.match(event.request);
  if (cached) return cached;

  const response = await fetch(event.request);
  await cache.put(event.request, response.clone());
  return response;
}

Chrome Route APIの使用(実験的)

if ('routes' in self.registration) {
  self.registration.routes.add(
    new URLPattern({ pathname: '/api/*' }),
    new CacheFirstStrategy({ cacheName: 'api-cache' })
  );
}

クライアントサイドルーティング

SPAルーターの実装

class Router {
  constructor() {
    this.routes = [];
    window.addEventListener('popstate', () => this.resolve());
  }

  add(pattern, handler) {
    this.routes.push({
      pattern: new URLPattern({ pathname: pattern }),
      handler
    });
    return this;
  }

  resolve(pathname = location.pathname) {
    for (const { pattern, handler } of this.routes) {
      const result = pattern.exec({ pathname });
      if (result) {
        handler(result.pathname.groups);
        return;
      }
    }
    console.warn(`No route matched: ${pathname}`);
  }

  navigate(path) {
    history.pushState(null, '', path);
    this.resolve(path);
  }
}

const router = new Router();
router
  .add('/dashboard', (params) => renderDashboard())
  .add('/users/:id', (params) => renderUser(params.id))
  .add('/posts/:slug/comments/:commentId?', (params) => renderComment(params))
  .add('/*', () => renderNotFound());

router.resolve();

パターンマッチング vs 従来手法の比較

特徴 URLPattern API RegExp path-to-regexp Express
ブラウザネイティブ
URLコンポーネント分割 ✅ プロト/ホスト/パス/クエリ 部分
名前付きキャプチャグループ ❌ (ES2025以前)
正規表現制約 :id(\\d+)
型安全な結果 ✅ groupsオブジェクト
パフォーマンス ネイティブC++実装 JSエンジン最適化 JS解析 JS解析

実用的パターンコレクション

const patterns = {
  apiV1: new URLPattern({ pathname: '/api/v1/:resource/:id?' }),
  apiV2: new URLPattern({ pathname: '/api/v2/:resource/:id?' }),
  userAssets: new URLPattern({ pathname: '/users/:userId/:type(img|doc|pdf)/*' }),
  localePage: new URLPattern({ pathname: '/:locale(en|zh|ja)/:page*' }),
  searchWithFilter: new URLPattern({ pathname: '/search', search: 'q=:query&filter=:filter?' }),
};

function matchRoute(url) {
  for (const [name, pattern] of Object.entries(patterns)) {
    const result = pattern.exec(url);
    if (result) return { name, groups: result.pathname.groups, search: result.search.groups };
  }
  return null;
}

ブラウザ互換性とポリフィル

ブラウザ サポート 備考
Chrome 95+ 完全サポート
Edge 95+ 完全サポート
Firefox ⚠️ ポリフィル必要
Safari 17+ 完全サポート
npm install urlpattern-polyfill
import { URLPattern } from 'urlpattern-polyfill';
if (!globalThis.URLPattern) {
  globalThis.URLPattern = URLPattern;
}

ベストプラクティスまとめ

  1. 名前付きグループを優先—インデックスアクセスより可読性と保守性が高い
  2. 動的セグメントに正規表現制約を適用—無効なマッチがハンドラに入るのを防止
  3. SWルートを優先度順に配置—APIルートをワイルドカードフォールバックより前に
  4. URLコンポーネントマッチングを活用—プロトコルとホスト名パターンでセキュアオリジンを固定
  5. プログレッシブエンハンスメント:APIの可用性を検出し、不可用時はRegExpにフォールバック

URLPattern APIはルーティングマッチングをフレームワーク依存から解放し、ブラウザインフラの一部とします。Service Workerと組み合わせることで、オフラインファーストアーキテクチャにネイティブルーティングレイヤーを提供します。

ブラウザローカルツールを無料で試す →

#URLPattern#路由匹配#Service Worker#Web API#模式匹配