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;
}
ベストプラクティスまとめ
- 名前付きグループを優先—インデックスアクセスより可読性と保守性が高い
- 動的セグメントに正規表現制約を適用—無効なマッチがハンドラに入るのを防止
- SWルートを優先度順に配置—APIルートをワイルドカードフォールバックより前に
- URLコンポーネントマッチングを活用—プロトコルとホスト名パターンでセキュアオリジンを固定
- プログレッシブエンハンスメント:APIの可用性を検出し、不可用時はRegExpにフォールバック
URLPattern APIはルーティングマッチングをフレームワーク依存から解放し、ブラウザインフラの一部とします。Service Workerと組み合わせることで、オフラインファーストアーキテクチャにネイティブルーティングレイヤーを提供します。
ブラウザローカルツールを無料で試す →
#URLPattern#路由匹配#Service Worker#Web API#模式匹配