Skip to main content

ルーティング

Edit this page on GitHub

Sveltekitの核心は、 ファイルシステムベースのルーター です。これは、アプリケーション構造がコードベースの構造(具体的には src/routes のコンテンツ)によって定義されることを意味します。

プロジェクトのコンフィグ を編集することで、これを異なるディレクトリに変更できます。

ルートには、ページ(pages)エンドポイント(endpoints) の2つのタイプがあります。

ページは通常、ユーザーに表示するHTML(及びページに必要なCSSやJavaScript)を生成します。デフォルトでは、ページはクライアントとサーバーの両方でレンダリングされますが、この動作は設定によって変更可能です。

エンドポイントは、サーバー上でのみ実行されます(もしくはサイトをビルドするときにプリレンダリングしている場合)。これは、プライベートな認証情報を必要とするデータベースやAPIにアクセスする場合や、本番環境ネットワーク上のマシンにあるデータを返す場合などに使用されます。ページはエンドポイントにデータをリクエストすることができます。エンドポイントはデフォルトではJSONを返しますが、他のフォーマットでもデータを返すことができます。

ページ(Pages)permalink

ページ(Pages)は .svelte ファイル (またはconfig.extensions に記載されている拡張子のファイル) に書かれているSvelteコンポーネントです。デフォルトでは、ユーザーが初めてアプリにアクセスすると、サーバーレンダリングバージョンのページと、そのページを'ハイドレート(hydrate)'しクライアントサイドルーターを初期化するJavaScriptが提供されます。それ以降、他のページへのナビゲーションは全てクライアント側で処理され、ページの共通部分を再レンダリングする必要がなくなるため、高速でアプリのような操作感になります。

ファイル名でルート(route)が決まります。例えば、src/routes/index.svelte はサイトのルート(root)になります。

src/routes/index.svelte
<svelte:head>
  <title>Welcome</title>
</svelte:head>

<h1>Hello and welcome to my site!</h1>

<a href="/about">About my site</a>

src/routes/about.sveltesrc/routes/about/index.svelte はどちらも /about ルート(route)になります。

src/routes/about.svelte
<svelte:head>
  <title>About</title>
</svelte:head>

<h1>About this site</h1>
<p>TODO...</p>

<a href="/">Home</a>

SvelteKit ではルート(routes)間のナビゲートに、フレームワーク固有の <Link> コンポーネントではなく、<a> 要素を使用します。

動的なパラメータは [カッコ] を使用してエンコードされます。例えば、ブログ記事の場合は src/routes/blog/[slug].svelte のように定義することがあるでしょう。これらのパラメータは、load 関数の中からアクセスできますし、page ストアを使用してアクセスすることもできます。

ルート(route)は動的なパラメータを複数持つことができます。例えば、src/routes/[category]/[item].sveltesrc/routes/[category]-[item].svelte といった具合です。(パラメータは 'non-greedy' です; x-y-z のような曖昧なケースの場合、categoryxitemy-z になります。)

エンドポイント(Endpoints)permalink

エンドポイント(Endpoints)は .js (または .ts) ファイルに記述されるモジュールで、HTTP メソッドに対応する request handler 関数をエクスポートします。サーバー上でのみ利用可能なデータ(例えば、データベースやファイルシステムにあるデータ) を読み書きできるようにするという役割があります。

src/routes/items/[id].js
ts
import db from '$lib/database';
 
/** @type {import('./__types/[id]').RequestHandler} */
export async function get({ params }) {
// `params.id` comes from [id].js
const item = await db.get(params.id);
 
if (item) {
return {
body: { item }
};
}
 
return {
status: 404
};
}

$lib のインポートについては心配無用です、それについては後ほど触れます。

上記の get 関数の型は、SvelteKit が (outDir の中に、rootDirs オプションを使用して) 生成する ./[id].d.ts ファイルにあり、params にアクセスするときの型安全性を提供します。詳細は generated types をご覧ください。

この request handler の役割は、レスポンスを表す { status, headers, body } オブジェクトを返すことです。statusHTTPステータスコードです。

  • 2xx — 成功レスポンス (デフォルトは 200)
  • 3xx — リダイレクション (location ヘッダーが必要です)
  • 4xx — クライアントエラー
  • 5xx — サーバーエラー

ページエンドポイント(Page endpoints)permalink

エンドポイントとページのファイル名が(拡張子以外)同一である場合、そのページはその同名のファイルを持つエンドポイントからプロパティ(props)を取得します (クライアントサイドナビゲーションの時は fetch が使用され、SSRの時には直接その関数を呼び出します)。

src/routes/items/[id].svelte というページの場合は、上記のエンドポイント(src/routes/items/[id].js)の body からプロパティを取得します:

src/routes/items/[id].svelte
<script>
  // エンドポイント(endpoint)からデータが取得される  export let item;
</script>

<h1>{item.title}</h1>

ページとエンドポイントが同じURLになるため、ページから HTML を取得するのではなくエンドポイントから JSON を取得するときは accept: application/json ヘッダーを付ける必要があります。また、URL に /__data.json を追加することで(例: /items/[id]/__data.json)、生データ(raw data)を取得できます。

スタンドアロンエンドポイント(Standalone endpoints)permalink

通常、エンドポイントはペアとなるページにデータを提供するために置きます。しかし、ページとは独立して置くこともできます。スタンドアロンエンドポイント(Standalone endpoints)は、返す body の型について少し柔軟で、オブジェクトに加えて、Uint8Array を返すこともできます。

スタンドアロンエンドポイントには必要に応じてファイル拡張子を付けることができますし、付けなければ直接アクセスすることができます:

ファイル名 エンドポイント
src/routes/data/index.json.js /data.json
src/routes/data.json.js /data.json
src/routes/data/index.js /data
src/routes/data.js /data

streaming request body、response body については今後サポートされる予定です。

POST, PUT, PATCH, DELETEpermalink

エンドポイント(Endpoints)は、HTTP メソッドに対応する関数をエクスポートすることで、GET だけでなく任意の HTTP メソッドを扱うことができます:

ts
export function post(event) {...}
export function put(event) {...}
export function patch(event) {...}
export function del(event) {...} // `delete` は予約語

get と同様、これらの関数は body を返すことができ、それをページにプロパティとして渡されます。get からの 4xx/5xx レスポンスはエラーページのレンダリングとなりますが、GET 以外のリクエストに対する同様のレスポンスはそうならないので、フォームのバリデーションエラーのレンダリングのようなことを行うことができます:

src/routes/items.js
ts
import * as db from '$lib/database';
 
/** @type {import('./__types/items').RequestHandler} */
export async function get() {
const items = await db.list();
 
return {
body: { items }
};
}
 
/** @type {import('./__types/items').RequestHandler} */
export async function post({ request }) {
const [errors, item] = await db.create(request);
 
if (errors) {
// バリデーションエラーを返します
return {
status: 400,
body: { errors }
};
}
 
// 新たに作成された item にリダイレクトします
return {
status: 303,
headers: {
location: `/items/${item.id}`
}
};
}
src/routes/items.svelte
<script>
  // このページでは常に `get` で取得したプロパティにアクセスします…  export let items;
  // …それに加えて、POST リクエストに応答してページがレンダリングされるときに  // `post` からプロパティを取得します  // 例えば、以下のようなフォームを送信したあとです  export let errors;
</script>

{#each items as item}
  <Preview item={item}/>
{/each}

<form method="post">
  <input name="title">

  {#if errors?.title}
    <p class="error">{errors.title}</p>
  {/if}

  <button type="submit">Create item</button>
</form>

Body parsingpermalink

request オブジェクトは標準の Request クラスのインスタンスです。そのため、簡単に request の body にアクセスできます:

ts
/** @type {import('@sveltejs/kit').RequestHandler} */
export async function post({ request }) {
const data = await request.formData(); // or .json(), or .text(), etc
 
await create(data);
return { status: 201 };
}

Setting cookiespermalink

エンドポイント(Endpoints) は set-cookie を含む headers オブジェクトを返すことで、Cookie を設定することができます。複数の Cookie を同時に設定するには、配列を返します:

ts
/** @type {import('@sveltejs/kit').RequestHandler} */
export function get() {
return {
headers: {
'set-cookie': [cookie1, cookie2]
}
};
}

HTTP method overridespermalink

HTML <form> 要素は、ネイティブでは GETPOST メソッドのみをサポートしています。例えば PUTDELETE などのその他のメソッドを許可するには、それを configuration で指定し、_method=VERB パラメータ (パラメータ名は設定で変更できます) を form の action に追加してください:

svelte.config.js
ts
/** @type {import('@sveltejs/kit').Config} */
const config = {
kit: {
methodOverride: {
allowed: ['PUT', 'PATCH', 'DELETE']
}
}
};
 
export default config;
<form method="post" action="/todos/{id}?_method=PUT">
  <!-- form elements --></form>

ネイティブの <form> の挙動を利用することで、JavaScript が失敗したり無効になっている場合でもアプリが動作し続けられます。

プライベートモジュールpermalink

名前が _. で始まるファイルやディレクトリ(.well-known は除く) はデフォルトでプライベートで、ルート(routes)を作成しません(ルートを作成するファイルからインポートすることは可能です)。どのモジュールをパブリックまたはプライベートとみなすかについては ルート(routes) 設定で設定することができます。

高度なルーティングpermalink

Restパラメータpermalink

ルート(route) セグメントの数がわからない場合は、rest 構文を使用することができます。例えば GitHub のファイルビューアのようなものを実装する場合には…

/[org]/[repo]/tree/[branch]/[...file]

…この場合、/sveltejs/kit/tree/master/documentation/docs/01-routing.md をリクエストすると、以下のパラメータをページで使うことができます。

ts
{
org: 'sveltejs',
repo: 'kit',
branch: 'master',
file: 'documentation/docs/01-routing.md'
}

src/routes/a/[...rest]/z.svelte/a/z (つまり、パラメータがない場合) にマッチしますし、/a/b/z/a/b/c/z などにもマッチします。rest パラメータの値が有効であることを、例えば matcher などを使用して、確実にチェックしてください。

Matchingpermalink

src/routes/archive/[page] のようなルート(route)は /archive/3 にマッチしますが、/archive/potato にもマッチしてしまいます。これを防ぎたい場合、パラメータ文字列("3""potato")を引数に取ってそれが有効なら true を返す matcherparams ディレクトリに追加することで、ルート(route)のパラメータを適切に定義することができます…

src/params/integer.js
ts
/** @type {import('@sveltejs/kit').ParamMatcher} */
export function match(param) {
return /^\d+$/.test(param);
}

…そしてルート(routes)を拡張します:

src/routes/archive/[page]
src/routes/archive/[page=integer]

もしパス名がマッチしない場合、SvelteKit は (後述のソート順の指定に従って) 他のルートでマッチするか試行し、どれにもマッチしない場合は最終的に 404 を返します。

Matcher は サーバーとブラウザの両方で動作します。

ソートpermalink

あるパスに対し、マッチするルート(routes)は複数でも構いません。例えば、これらのルート(routes)はどれも /foo-abc にマッチします:

src/routes/[...catchall].svelte
src/routes/[a].js
src/routes/[b].svelte
src/routes/foo-[c].svelte

SvelteKit は、どのルート(route)に対してリクエストされているのかを判断しなければなりません。そのため、以下のルールに従ってこれらをソートします…

  • より詳細・明確(specific)なルート(routes)ほど、より優先度が高い
  • Standalone endpoints は、同じ詳細度(specificity)のページよりも優先度が高い
  • matchers 付きのパラメータ ([name=type]) は matchers なしのパラメータ ([name]) よりも優先度が高い
  • Restパラメータは最も優先度が低い
  • 優先度が同じ場合はアルファベット順で解決される

…この順序で並べると、/foo-abc の場合は src/routes/foo-[bar].svelte を呼び出すことになります:

src/routes/foo-[c].svelte
src/routes/[a].js
src/routes/[b].svelte
src/routes/[...catchall].svelte
We stand with Ukraine. Donate → We stand with Ukraine. Petition your leaders. Show your support.