Svelte入門:SvelteKitでのフック処理 -Vol.1-

スポンサーリンク
Svelte入門:SvelteKitでのフック処理 -Vol.1- 用語解説
Svelte入門:SvelteKitでのフック処理 -Vol.1-
この記事は約10分で読めます。
よっしー
よっしー

こんにちは。よっしーです(^^)

今日は、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アプリケーションでサーバーサイドの処理をカスタマイズするための非常に重要な機能です。この関数を使用することで、リクエストの処理方法を細かく制御できます。

主なポイントは以下の通りです:

  1. すべてのリクエストに対して実行されます(プリレンダリング中も含む)。
  2. リクエスト(event)とレスポンス生成関数(resolve)を受け取ります。
  3. レスポンスヘッダーやボディの修正、あるいはSvelteKitの通常の処理をバイパスすることができます。
  4. カスタムルートの実装にも使用できます。
  5. 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関数の追加パラメータについて詳しく解説しています。主なポイントは以下の通りです:

  1. event.localsオブジェクトを使用してカスタムデータを追加する方法
  2. 複数のhandle関数をsequenceヘルパー関数で実行する可能性
  3. resolve関数の第2引数として渡せるオプションの詳細
  • transformPageChunk:HTMLの変換
  • filterSerializedResponseHeaders:シリアライズされたレスポンスヘッダーのフィルタリング
  • preload:プリロードするファイルの決定
  1. エラー処理に関する注意点

これらの機能を使用することで、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"に設定されていない限り、cookieauthorizationヘッダーを転送します。

クロスオリジンリクエストの場合、リクエストURLがアプリのサブドメインに属している場合、cookieが含まれます。例えば、アプリがmy-domain.comにあり、APIがapi.my-domain.comにある場合、cookieはリクエストに含まれます。

アプリとAPIが兄弟サブドメイン(例えばwww.my-domain.comapi.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リクエストの動作を細かく制御できます。

主なポイントは以下の通りです:

  1. サーバーサイドまたはプリレンダリング中のfetchリクエストを変更できます。
  2. APIエンドポイントの変更や、内部ネットワークへの直接アクセスなどに使用できます。
  3. クレデンシャル(cookieやauthorizationヘッダー)の扱いを制御できます。

特に注目すべき点は、クロスオリジンリクエストでのcookieの扱いです:

  • アプリとAPIが同じドメインのサブドメインの場合、cookieは自動的に含まれます。
  • アプリとAPIが兄弟サブドメインの場合、共通の親ドメインのcookieは自動的には含まれません。この場合、handleFetchを使用して手動でcookieを含める必要があります。

この機能を使用することで、開発環境と本番環境でのAPIアクセスの違いを吸収したり、セキュリティを強化したりすることができます。

おわりに

今日は、 SvelteKitでのフック処理について解説しました。

よっしー
よっしー

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

それでは、また明日お会いしましょう(^^)

コメント

タイトルとURLをコピーしました