Advanced
Hooks
Edit this page on GitHub'Hooks' は、特定のイベントに対して SvelteKit がレスポンスを呼び出すことを宣言するアプリ全体の関数で、これによってフレームワークの動作をきめ細やかに制御できるようになります。
hooks ファイルは2つあり、どちらもオプションです:
src/hooks.server.js
— アプリのサーバーの hookssrc/hooks.client.js
— アプリのクライアントの hookssrc/hooks.js
— サーバーとクライアントの両方で実行される hooks
これらのモジュールのコードはアプリケーションの起動時に実行されるので、データベースクライアントの初期化などに有用です。
これらのファイルの場所は
config.kit.files.hooks
で設定できます。
Server hookspermalink
以下の hooks は src/hooks.server.js
に追加することができます:
handlepermalink
この関数は SvelteKit のサーバーが リクエスト を受けるたびに (アプリの実行中であろうと、プリレンダリングであろうと) 実行され、レスポンス を決定します。リクエストを表す event
オブジェクトと、ルート(route)をレンダリングしレスポンスを生成する resolve
という関数を受け取ります。これにより、レスポンスのヘッダーやボディを変更したり、SvelteKitを完全にバイパスすることができます (例えば、プログラムでルート(routes)を実装する場合など)。
ts
/** @type {import('@sveltejs/kit').Handle} */export async functionhandle ({event ,resolve }) {if (event .url .pathname .startsWith ('/custom')) {return newResponse ('custom response');}constresponse = awaitresolve (event );returnresponse ;}
ts
import type {Handle } from '@sveltejs/kit';export consthandle :Handle = async ({event ,resolve }) => {if (event .url .pathname .startsWith ('/custom')) {return newResponse ('custom response');}constresponse = awaitresolve (event );returnresponse ;};
静的アセット(プリレンダリング済みのページを含む)に対するリクエストは SvelteKit では処理されません。
未実装の場合、デフォルトは ({ event, resolve }) => resolve(event)
となります。カスタムデータをリクエストに追加し、+server.js
のハンドラーやサーバー(server) load
関数に渡すには、以下のように event.locals
オブジェクトに埋め込んでください。
ts
/** @type {import('@sveltejs/kit').Handle} */export async functionhandle ({event ,resolve }) {event .locals .user = awaitgetUserInformation (event .cookies .get ('sessionid'));constresponse = awaitresolve (event );response .headers .set ('x-custom-header', 'potato');returnresponse ;}
ts
import type {Handle } from '@sveltejs/kit';export consthandle :Handle = async ({event ,resolve }) => {event .locals .user = awaitgetUserInformation (event .cookies .get ('sessionid'));constresponse = awaitresolve (event );response .headers .set ('x-custom-header', 'potato');returnresponse ;};
sequence
ヘルパー関数を使用すると、複数の handle
関数を定義することができます。
resolve
はオプションの第2引数をサポートしており、レスポンスのレンダリング方法をより詳細にコントロールすることができます。そのパラメータは、以下のフィールドを持つオブジェクトです:
transformPageChunk(opts: { html: string, done: boolean }): MaybePromise<string | undefined>
— カスタムの変換を HTML に適用します。done
が true である場合、それは最後のチャンクです。チャンクが整形された HTML であることは保証されませんが (例えば、要素の開始タグは含むが終了タグは含まれない、など)、常に%sveltekit.head%
やレイアウト(layout)/ページ(page)コンポーネントなどのような理にかなった境界 (sensible boundaries) で分割されます。filterSerializedResponseHeaders(name: string, value: string): boolean
—load
関数がfetch
でリソースを読み込むときに、シリアライズされるレスポンスにどのヘッダーを含めるかを決定します。デフォルトでは何も含まれません。preload(input: { type: 'js' | 'css' | 'font' | 'asset', path: string }): boolean
—<head>
タグにどのファイルをプリロードの対象として追加するか決定します。このメソッドはビルド時、コードチャンクを構築している際に見つかったファイルごとに呼び出されます。これにより、例えば+page.svelte
にimport './styles.css
がある場合、そのページに訪れたときにその CSS ファイルへの解決されたパスを以てpreload
が呼び出されるようになります。これはビルド時の分析によって行われるため、開発モードではpreload
が呼ばれないことにご注意ください。プリロードによってその対象がより早くダウンロードされるようになるためパフォーマンスが改善しますが、不必要に多くのものをダウンロードしてしまうと、core web vitals を悪化させてしまいます。デフォルトでは、js
、css
ファイルがプリロードされます。現時点ではasset
ファイルはプリロードされませんが、フィードバックによっては追加されるかもしれません。
ts
/** @type {import('@sveltejs/kit').Handle} */export async functionhandle ({event ,resolve }) {constresponse = awaitresolve (event , {transformPageChunk : ({html }) =>html .replace ('old', 'new'),filterSerializedResponseHeaders : (name ) =>name .startsWith ('x-'),preload : ({type ,path }) =>type === 'js' ||path .includes ('/important/')});returnresponse ;}
ts
import type {Handle } from '@sveltejs/kit';export consthandle :Handle = async ({event ,resolve }) => {constresponse = awaitresolve (event , {transformPageChunk : ({html }) =>html .replace ('old', 'new'),filterSerializedResponseHeaders : (name ) =>name .startsWith ('x-'),preload : ({type ,path }) =>type === 'js' ||path .includes ('/important/'),});returnresponse ;};
resolve(...)
は決してエラーをスローせず、適切なステータスコードと Promise<Response>
を返すことにご注意ください。もし handle
中に他の場所でエラーがスローされた場合、それは致命的(fatal)なものとして扱われ、SvelteKit は Accept
ヘッダーに応じて、そのエラーの JSON 表現か、src/error.html
でカスタマイズ可能なフォールバックエラーページをレスポンスとして返します。エラーハンドリングの詳細は こちら からお読み頂けます。
handleFetchpermalink
この関数は、サーバー上で (またはプリレンダリング中に) 実行される load
関数や action
関数の中で発生する fetch
リクエストを変更 (または置換) することできます。
例えば、ユーザーがクライアントサイドでそれぞれのページに移動する際に、load
関数で https://api.yourapp.com
のようなパブリックな URL にリクエストを行うかもしれませんが、SSR の場合には (パブリックなインターネットとの間にあるプロキシやロードバランサーをバイパスして) API を直接呼ぶほうが理にかなっているでしょう。
ts
/** @type {import('@sveltejs/kit').HandleFetch} */export async functionhandleFetch ({request ,fetch }) {if (request .url .startsWith ('https://api.yourapp.com/')) {// clone the original request, but change the URLrequest = newRequest (request .url .replace ('https://api.yourapp.com/', 'http://localhost:9999/'),request );}returnfetch (request );}
ts
import type {HandleFetch } from '@sveltejs/kit';export consthandleFetch :HandleFetch = async ({request ,fetch }) => {if (request .url .startsWith ('https://api.yourapp.com/')) {// clone the original request, but change the URLrequest = newRequest (request .url .replace ('https://api.yourapp.com/', 'http://localhost:9999/'),request ,);}returnfetch (request );};
Credentials
同一オリジン(same-origin)リクエストの場合、SvelteKit の fetch
実装は、credentials
オプションを "omit"
にしない限り、 cookie
と authorization
ヘッダーを転送します。
クロスオリジン(cross-origin)リクエストの場合、リクエスト URL がアプリのサブドメインに属するときは cookie
はリクエストに含まれます。例えば、あなたのアプリが my-domain.com
にあり、あなたの API が api.my-domain.com
にある場合、cookie はリクエストに含まれることになります。
もしあなたのアプリと API が兄弟関係にあるサブドメイン (例えば www.my-domain.com
と api.my-domain.com
) の場合は、my-domain.com
のような共通の親ドメインに属する cookie は含まれません、なぜなら SvelteKit にはその cookie がどのドメインに属するか判断する方法がないからです。こういったケースでは、handleFetch
を使って手動で cookie を含める必要があります:
ts
/** @type {import('@sveltejs/kit').HandleFetch} */export async functionhandleFetch ({event ,request ,fetch }) {if (request .url .startsWith ('https://api.my-domain.com/')) {Argument of type 'string | null' is not assignable to parameter of type 'string'. Type 'null' is not assignable to type 'string'.2345Argument of type 'string | null' is not assignable to parameter of type 'string'. Type 'null' is not assignable to type 'string'.request .headers .set ('cookie',event .request .headers .get ('cookie'));}returnfetch (request );}
ts
import type {HandleFetch } from '@sveltejs/kit';export consthandleFetch :HandleFetch = async ({event ,request ,fetch }) => {if (request .url .startsWith ('https://api.my-domain.com/')) {Argument of type 'string | null' is not assignable to parameter of type 'string'. Type 'null' is not assignable to type 'string'.2345Argument of type 'string | null' is not assignable to parameter of type 'string'. Type 'null' is not assignable to type 'string'.request .headers .set ('cookie',event .request .headers .get ('cookie'));}returnfetch (request );};
Shared hookspermalink
以下は src/hooks.server.js
と src/hooks.client.js
のどちらにも追加できます:
handleErrorpermalink
予期せぬエラーがロード中またはレンダリング中にスローされると、この関数が error
、event
、status
コード、message
を引数にとって呼び出されます。これによって以下の2つのことが可能になります:
- エラーをログに残すことができます
- エラーからメッセージやスタックトレースなどの機密情報を省略し、ユーザーに見せても安全なカスタムの表現を生成することができます。戻り値のデフォルトは
{ message }
で、$page.error
の値となります。
あなたのコード (またはあなたのコードから呼び出されたライブラリのコード) からスローされたエラーの場合、ステータスは 500 となり、message は "Internal Error" になります。error.message
にはユーザーに公開されるべきではない機密情報が含まれている可能性がありますが、message
は安全です (一般的なユーザーにとっては無意味ではありますが)。
$page.error
オブジェクトに型安全な方法で情報を追加するには、App.Error
interface を宣言することで想定する形にすることができます (適切なフォールバックの動作を保証するため、message: string
を含む必要があります)。これにより、例えばユーザーがテクニカルサポートスタッフとの対応の際に引用することができるトラッキング ID を付加することができます:
ts
declareglobal {namespaceApp {interfaceError {message : string;errorId : string;}}}export {};
ts
import * asSentry from '@sentry/sveltekit';Sentry .init ({/*...*/})/** @type {import('@sveltejs/kit').HandleServerError} */export async functionhandleError ({error ,event ,status ,message }) {consterrorId =crypto .randomUUID ();// example integration with https://sentry.io/Object literal may only specify known properties, and 'errorId' does not exist in type 'Error'.2353Object literal may only specify known properties, and 'errorId' does not exist in type 'Error'.Sentry .captureException (error , {extra : {event ,errorId ,status }});return {message : 'Whoops!',errorId };}
ts
import * asSentry from '@sentry/sveltekit';import type {HandleServerError } from '@sveltejs/kit';Sentry .init ({/*...*/});export consthandleError :HandleServerError = async ({error ,event ,status ,message }) => {consterrorId =crypto .randomUUID ();// example integration with https://sentry.io/Sentry .captureException (error , {extra : {event ,errorId ,status },});return {message : 'Whoops!',errorId ,};};
ts
import * asSentry from '@sentry/sveltekit';Sentry .init ({/*...*/})/** @type {import('@sveltejs/kit').HandleClientError} */export async functionhandleError ({error ,event ,status ,message }) {consterrorId =crypto .randomUUID ();// example integration with https://sentry.io/Object literal may only specify known properties, and 'errorId' does not exist in type 'Error'.2353Object literal may only specify known properties, and 'errorId' does not exist in type 'Error'.Sentry .captureException (error , {extra : {event ,errorId ,status }});return {message : 'Whoops!',errorId };}
ts
import * asSentry from '@sentry/sveltekit';import type {HandleClientError } from '@sveltejs/kit';Sentry .init ({/*...*/});export consthandleError :HandleClientError = async ({error ,event ,status ,message }) => {consterrorId =crypto .randomUUID ();// example integration with https://sentry.io/Sentry .captureException (error , {extra : {event ,errorId ,status },});return {message : 'Whoops!',errorId ,};};
src/hooks.client.js
では、handleError
の型はHandleServerError
ではなくHandleClientError
で、event
はRequestEvent
ではなくNavigationEvent
です。
この関数は 想定される エラー (@sveltejs/kit
からインポートされる error
関数でスローされるエラー) の場合は呼び出されません。
開発中、Svelte のコードの構文エラーでエラーが発生した場合、渡される error には、エラーの場所のハイライトが付与された frame
プロパティがあります。
handleError
自体が決してエラーをスローしないようにしてください。
Universal hookspermalink
以下は src/hooks.js
に追加することができます。universal hooks はサーバーとクライアントの両方で実行されます (shared hooks と混同しないようにしてください、shared hooks は環境依存です)。
reroutepermalink
この関数は handle
より前に実行され、URL をルート(route)に変換する方法を変更することができます。戻り値の pathname (デフォルトは url.pathname
) はルート(route)パラメータを選択するのに使用されます。
例えば、src/routes/[[lang]]/about/+page.svelte
というページがあるとして、/en/about
や /de/ueber-uns
や /fr/a-propos
でアクセスできるようにしたいとします。この場合は reroute
を使用して実装することができます:
ts
/** @type {Record<string, string>} */consttranslated = {'/en/about': '/en/about','/de/ueber-uns': '/de/about','/fr/a-propos': '/fr/about',};/** @type {import('@sveltejs/kit').Reroute} */export functionreroute ({url }) {if (url .pathname intranslated ) {returntranslated [url .pathname ];}}
ts
import type {Reroute } from '@sveltejs/kit';consttranslated :Record <string, string> = {'/en/about': '/en/about','/de/ueber-uns': '/de/about','/fr/a-propos': '/fr/about',};export constreroute :Reroute = ({url }) => {if (url .pathname intranslated ) {returntranslated [url .pathname ];}};
lang
パラメータは戻り値の pathname から正しく導くことができます。
reroute
を使用してもブラウザのアドレスバーの内容や event.url
の値は変更されません。