SvelteKitの高度なルーティング(オプショナルパラメータ・レストパラメータ・マッチャー)を整理した話

SvelteKitの応用的なルーティングパターンを整理します。ページネーションや可変長パスなど、実務でよく必要になる場面で使います。


① オプショナルパラメータ [[param]]

[] を二重にすると省略可能なパラメータになります。

src/routes/
  list/
    [[page]]/
      +page.server.ts
      +page.svelte
/list      → page = undefined(省略時)
/list/2    → page = '2'
/list/3    → page = '3'

ページネーションの実装例:

// src/routes/list/[[page]]/+page.server.ts
import type { PageServerLoad } from './$types';

export const load: PageServerLoad = async ({ params, platform }) => {
  const page = Number(params.page ?? 1);  // 省略時は1ページ目
  const limit = 2;
  const offset = (page - 1) * limit;

  const db = platform?.env.DB;

  const result = await db?.prepare(
    'SELECT * FROM posts LIMIT ? OFFSET ?'
  ).bind(limit, offset).all();

  const total = await db?.prepare(
    'SELECT COUNT(*) as count FROM posts'
  ).first<{ count: number }>();

  const totalPages = Math.ceil((total?.count ?? 0) / limit);

  return { posts: result?.results ?? [], page, totalPages };
};
<!-- src/routes/list/[[page]]/+page.svelte -->
<script lang="ts">
  let { data } = $props();
</script>

<h1>記事一覧({data.page} / {data.totalPages} ページ)</h1>

<ul>
  {#each data.posts as post}
    <li>{post.title}</li>
  {/each}
</ul>

<nav>
  {#if data.page > 1}
    <a href="/list/{data.page - 1}">← 前のページ</a>
  {/if}
  {#if data.page < data.totalPages}
    <a href="/list/{data.page + 1}">次のページ →</a>
  {/if}
</nav>

② レストパラメータ [...rest]

可変長のパスを受け取ります。

src/routes/
  files/
    [...path]/
      +page.svelte
// /files/a/b/c → params.path = 'a/b/c'
export const load: PageServerLoad = async ({ params }) => {
  const pathParts = params.path.split('/');
  return { pathParts };
};

③ パラメータマッチャー

パラメータの形式を制限できます。数字以外のIDでアクセスされたとき404を返すなどに使います。

// src/params/integer.ts
import type { ParamMatcher } from '@sveltejs/kit';

export const match: ParamMatcher = (param) => {
  return /^\d+$/.test(param);  // 数字のみ許可
};
src/routes/
  posts/
    [id=integer]/    ← id が数字のときだけマッチ
      +page.svelte
/posts/1    → マッチ ✅
/posts/abc  → マッチしない(404)❌

④ ルートグループ (group) の実用パターン

URLに影響を与えずレイアウトを分けます。

src/routes/
  (public)/
    +layout.svelte    ← ヘッダーあり
    +page.svelte      ← /
    about/
      +page.svelte    ← /about
  (authed)/
    +layout.svelte    ← 認証チェック
    dashboard/
      +page.svelte    ← /dashboard

Cloudflare環境での注意点

adapter-cloudflare + wrangler dev を使う場合、新しいルートを追加したときはビルドが必要です。

npm run dev(Vite)
  → ファイル変更をリアルタイムで検知・反映
  → 新しいルートも即座に認識

npx wrangler dev
  → ビルド済みの出力を使って動かす
  → 新しいルートはビルドしないと認識されない(404になる)

推奨する開発フロー:

# 通常の開発はViteを使う
npm run dev

# D1など Cloudflare 固有の機能が必要なとき
npm run build
npx wrangler dev

まとめ

構文 用途
[param] /posts/[id] 必須パラメータ
[[param]] /list/[[page]] 省略可能なパラメータ(ページネーションなど)
[...rest] /files/[...path] 可変長パス
[param=matcher] /posts/[id=integer] 形式を制限する
(group) (authed)/dashboard URLに影響しないグループ化
← トップページに戻る