
こんにちは。よっしーです(^^)
今日は、SvelteKitでのフック処理について解説しています。
背景
SvelteKitでのフック処理について調査する機会がありましたので、その時の内容を備忘として記事に残しました。
フックとは
「フック」とは、特定のイベントに応じてSvelteKitが呼び出すアプリ全体で宣言する関数であり、フレームワークの動作を細かく制御することができます。
3つのフックファイルがあり、すべてオプションです:
src/hooks.server.js— アプリのサーバーフックsrc/hooks.client.js— アプリのクライアントフックsrc/hooks.js— クライアントとサーバーの両方で実行されるアプリのフック
これらのモジュール内のコードはアプリケーションの起動時に実行されるため、データベースクライアントの初期化などに役立ちます。
これらのファイルの場所はconfig.kit.files.hooksで設定できます。
サーバフックとは
次のフックを src/hooks.server.js に追加できます。
handle
この関数は、SvelteKitサーバーがリクエストを受信するたびに実行されます(アプリの実行中やプリレンダリング中に発生します)。この関数は、リクエストに対するレスポンスを決定します。eventオブジェクト(リクエストを表す)とresolveという関数(ルートをレンダリングし、Responseを生成する)を受け取ります。これにより、レスポンスヘッダーやボディを変更したり、SvelteKitを完全にバイパスしたりすることができます(例えば、プログラムでルートを実装する場合など)。
src/hooks.server.js
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
if (event.url.pathname.startsWith('/custom')) {
return new Response('custom response');
}
const response = await resolve(event);
return response;
}
静的アセット(すでにプリレンダリングされたページを含む)へのリクエストは、SvelteKitによって処理されません。
handle関数は、SvelteKitアプリケーションでサーバーサイドの処理をカスタマイズするための非常に重要な機能です。この関数を使用することで、リクエストの処理方法を細かく制御できます。
主なポイントは以下の通りです:
- すべてのリクエストに対して実行されます(プリレンダリング中も含む)。
- リクエスト(
event)とレスポンス生成関数(resolve)を受け取ります。 - レスポンスヘッダーやボディの修正、あるいはSvelteKitの通常の処理をバイパスすることができます。
- カスタムルートの実装にも使用できます。
event.localsオブジェクトを使用して、カスタムデータをリクエストに追加できます。
この機能を使用することで、認証、ロギング、カスタムヘッダーの追加など、さまざまなサーバーサイドの処理をアプリケーション全体に適用することができます。
実装されていない場合、デフォルトは({ event, resolve }) => resolve(event)となります。リクエストにカスタムデータを追加するには(これは+server.jsのハンドラーやサーバーのload関数に渡されます)、以下のようにevent.localsオブジェクトにデータを設定します。
src/hooks.server.js
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
event.locals.user = await getUserInformation(event.cookies.get('sessionid'));
const response = await resolve(event);
response.headers.set('x-custom-header', 'potato');
return response;
}
複数のhandle関数を定義し、sequenceヘルパー関数を使用して実行することもできます。
resolve関数は、レスポンスのレンダリング方法をより詳細に制御するための2つ目のオプションパラメータをサポートしています。このパラメータは以下のフィールドを持つオブジェクトです:
transformPageChunk(opts: { html: string, done: boolean }): MaybePromise<string | undefined>— HTMLにカスタム変換を適用します。filterSerializedResponseHeaders(name: string, value: string): boolean— シリアライズされたレスポンスに含めるヘッダーを決定します。preload(input: { type: 'js' | 'css' | 'font' | 'asset', path: string }): boolean—<head>タグに追加してプリロードするファイルを決定します。
例:
/** @type {import('@sveltejs/kit').Handle} */
export async function handle({ event, resolve }) {
const response = await resolve(event, {
transformPageChunk: ({ html }) => html.replace('old', 'new'),
filterSerializedResponseHeaders: (name) => name.startsWith('x-'),
preload: ({ type, path }) => type === 'js' || path.includes('/important/')
});
return response;
}
注意:resolve(...)は決してエラーをスローしません。常に適切なステータスコードを持つPromise<Response>を返します。handle内の他の場所でエラーがスローされた場合、それは致命的なものとして扱われ、SvelteKitはエラーのJSON表現またはフォールバックエラーページ(src/error.htmlでカスタマイズ可能)で応答します。
handle関数のより高度な使用方法と、resolve関数の追加パラメータについて詳しく解説しています。主なポイントは以下の通りです:
event.localsオブジェクトを使用してカスタムデータを追加する方法- 複数の
handle関数をsequenceヘルパー関数で実行する可能性 resolve関数の第2引数として渡せるオプションの詳細
transformPageChunk:HTMLの変換filterSerializedResponseHeaders:シリアライズされたレスポンスヘッダーのフィルタリングpreload:プリロードするファイルの決定
- エラー処理に関する注意点
これらの機能を使用することで、SvelteKitアプリケーションのサーバーサイド処理をより細かく制御し、パフォーマンスの最適化やカスタムの動作を実装することができます。
handleFetch
この関数を使用すると、サーバー上(またはプリレンダリング中)で実行されるloadまたはaction関数内で発生するfetchリクエストを変更(または置換)することができます。
例えば、ユーザーがクライアントサイドでページにナビゲートする際に、load関数がhttps://api.yourapp.comのような公開URLにリクエストを行うかもしれません。しかし、SSR(サーバーサイドレンダリング)中には、APIに直接アクセスする(公開インターネットとの間にあるプロキシやロードバランサーをバイパスする)ことが理にかなっている場合があります。
src/hooks.server.js
/** @type {import('@sveltejs/kit').HandleFetch} */
export async function handleFetch({ request, fetch }) {
if (request.url.startsWith('https://api.yourapp.com/')) {
// 元のリクエストをクローンし、URLを変更
request = new Request(
request.url.replace('https://api.yourapp.com/', 'http://localhost:9999/'),
request
);
}
return fetch(request);
}
クレデンシャル
同一オリジンのリクエストの場合、SvelteKitのfetch実装は、credentialsオプションが"omit"に設定されていない限り、cookieとauthorizationヘッダーを転送します。
クロスオリジンリクエストの場合、リクエスト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を手動で含める必要があります:
src/hooks.server.js
/** @type {import('@sveltejs/kit').HandleFetch} */
export async function handleFetch({ event, request, fetch }) {
if (request.url.startsWith('https://api.my-domain.com/')) {
request.headers.set('cookie', event.request.headers.get('cookie'));
}
return fetch(request);
}
handleFetch関数は、SvelteKitアプリケーションでサーバーサイドのfetchリクエストをカスタマイズするための重要な機能です。この関数を使用することで、APIリクエストの動作を細かく制御できます。
主なポイントは以下の通りです:
- サーバーサイドまたはプリレンダリング中の
fetchリクエストを変更できます。 - APIエンドポイントの変更や、内部ネットワークへの直接アクセスなどに使用できます。
- クレデンシャル(cookieやauthorizationヘッダー)の扱いを制御できます。
特に注目すべき点は、クロスオリジンリクエストでのcookieの扱いです:
- アプリとAPIが同じドメインのサブドメインの場合、cookieは自動的に含まれます。
- アプリとAPIが兄弟サブドメインの場合、共通の親ドメインのcookieは自動的には含まれません。この場合、
handleFetchを使用して手動でcookieを含める必要があります。
この機能を使用することで、開発環境と本番環境でのAPIアクセスの違いを吸収したり、セキュリティを強化したりすることができます。
おわりに
今日は、 SvelteKitでのフック処理について解説しました。

何か質問や相談があれば、コメントをお願いします。また、エンジニア案件の相談にも随時対応していますので、お気軽にお問い合わせください。
それでは、また明日お会いしましょう(^^)


コメント