サイトロゴ
SvelteKit Created: 2026/05/31 Updated: 2026/06/02

SvelteKit Hooks入門:hooks.server.ts / hooks.client.ts / hooks.ts の違いと handle の基本

SvelteKitのHooksとは何かを初心者向けに解説。hooks.server.ts、hooks.client.ts、hooks.tsの違いや、handle・event・resolve・localsの基本を実装例とともに整理します。

シリーズ:SvelteKit Hooks 完全ガイド

01

Hooks の基本と3ファイルの違い

02

handle で認証を一元化する

03

handleError・handleFetch・クライアント

前シリーズ「 SvelteKit サーバー・クライアント動作入門 (全3回)― ルーティング・load 関数・SSR の仕組み」を学んでいない方は前シリーズから読むことをおすすめします。

SvelteKit サーバー・クライアント動作入門

SvelteKit のファイルベースルーティング・load 関数によるデータ取得・サーバーとクライアントの境界を全3回で解説。+page.server.ts や SSR の仕組みを商品カタログアプリの実装を通じて基礎から学べるシリーズです。

前シリーズでは商品カタログアプリを通じて、ルーティング・load 関数・SSR の仕組みを学びました。ページにデータを渡すという「縦の流れ」は完成しています。

今回のシリーズBで扱う Hooks は、その流れに 横断的に割り込む 仕組みです。「すべてのリクエストでログを取りたい」「特定のページへのアクセスは必ず認証チェックを通したい」といった処理を、各ページの+page.server.ts に個別に書くのではなく、 1か所に集約できます。

💡 前提条件

前シリーズで作成したsvelte-shop/ プロジェクトを引き続き使います。npm run dev で開発サーバーが起動できる状態で進めてください。

load 関数と Hooks ― 何が違うのか

前シリーズで学んだload 関数と Hooks は、どちらもサーバー側の処理ですが役割がまったく異なります。

LOAD 関数(縦の流れ・ページごとに実行)

リクエスト

+layout.server.ts
load()

+page.server.ts
load()

+page.svelte

HOOKS(横断・すべてのリクエストで実行)

リクエスト

hooks.server.ts
handle() ← 割り込み

load → page
(通常のフロー)

レスポンス

load 関数 Hooks
定義する場所 各ページの+page.server.ts などsrc/hooks.server.ts など(1ファイル)
実行タイミング そのページへのアクセス時のみ すべてのリクエストで実行
主な用途 ページ固有のデータ取得 認証・ロギング・ヘッダー操作など横断処理
ページへのデータの渡し方return { ... } の戻り値event.locals 経由

3ファイルの違いと実行タイミング

Hooks には3種類のファイルがあります。それぞれ 動く場所と使えるフック関数 が異なります。

hooks.server.ts

🖥 サーバーのみ

handle

handleFetch

handleError

すべてのサーバーへのリクエストで実行される。認証チェック・リクエストロギング・レスポンスヘッダーの追加など、サーバーサイドの横断処理を書く。このシリーズで最も多く使うファイル。

hooks.client.ts

🌐 ブラウザのみ

handleError

ブラウザ上でのナビゲーション(ページ遷移)やレンダリング時に実行される。ブラウザで発生した未捕捉エラーをここで処理する。使えるフックはhandleError のみ。

hooks.ts

🔄 サーバー+ブラウザ

handleError

reroute

transport

サーバー・ブラウザ両方で動く universal なフック。reroute でURLを変換したり、transportでカスタムシリアライズを定義したりする。秘密情報は書けない点に注意。

💡 ファイルの置き場所

3ファイルはすべてsrc/ 直下に置きます。src/routes/ の中ではなく、src/hooks.server.ts という位置に置くことでSvelteKitが自動的に認識します。存在しない場合は単にスキップされるので、必要なファイルだけ作れば問題ありません。

text
src/
├─hooks.server.ts← 今回作成
├─hooks.client.ts← 第3回で作成
├─hooks.ts← 今回作成(reroute の実装例)
├─app.d.ts← locals の型定義を追加する
└─routes/← 前シリーズで作成済み

handle 関数の基本 ― event と resolve()

🖥 hooks.server.ts

handle 関数はhooks.server.ts の中核です。すべてのサーバーリクエストを受け取り、処理をして、レスポンスを返します。構造はシンプルで、 2つの引数(event resolve を受け取ります。

ts
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
  // ① リクエストへの前処理をここに書く
  console.log(`[handle] ${event.request.method} ${event.url.pathname}`);

  // ② resolve() を呼ぶと「通常の処理(load → page のフロー)」が実行される
    const response = await resolve(event);

  // ③ レスポンスへの後処理をここに書く
  response.headers.set('X-Powered-By', 'SvelteKit');

  return response;
};

resolve(event) の呼び出しが 最重要ポイント です。これを呼ぶとload 関数・ページレンダリングなど通常のフローが走り、レスポンスが返ってきます。呼ばなければページは表示されません。

⚠️ ️ resolve() を必ず呼ぶこと

handle の中でresolve(event) を呼び忘れると、すべてのページが真っ白になります。認証チェックでredirect() を返す場合など、意図的にresolve をスキップするケース以外は必ず呼んでください。

event オブジェクトの主要プロパティ

event はリクエストに関するあらゆる情報を持つオブジェクトです。シリーズAのload 関数で受け取っていたevent と同じ型です。

event.request

Request

Web 標準の Request オブジェクト。メソッド・ヘッダー・ボディを取得できる。

event.url

URL

アクセスされたURL。pathname searchParams などを持つ。

event.params

Record<string, string>

動的ルートのパラメータ。[id] など。

event.cookies

Cookies

Cookie の読み書き。第2回で認証に使う。

event.locals

App.Locals

handle → load → page へデータを受け渡すための入れ物。次のセクションで詳解。

event.fetch

Function

サーバーサイドで使える fetch。第3回のhandleFetch で詳解。


locals ― handle からページへデータを渡す

event.locals handle 関数からページのload 関数へデータを受け渡すための専用オブジェクトです。たとえばhandle で認証済みユーザー情報をlocals にセットすれば、各ページのload 関数で同じ検証コードを繰り返さずに使えます。

Step 1 ― app.d.ts で型を定義する

locals に何を入れるかをTypeScriptに伝えるため、src/app.d.ts に型定義を追加します。

ts
// See https://svelte.dev/docs/kit/types#app-d-ts
declare global {
  namespace App {
    interface Locals {
      // handle でセットするユーザー情報の型
            user: { id: string; name: string } | null;
    }
    // interface Error {}
    // interface PageData {}
    // interface Platform {}
  }
}
export {};

Step 2 ― handle で locals にセットする

ts
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
  // Cookie からセッションIDを取得(第2回で本格実装)
  // 今はダミーとして固定値をセット
    event.locals.user = { id: 'user-1', name: '山田 太郎' };

  return resolve(event);
};

Step 3 ― load 関数で locals を受け取る

ts
import type { PageServerLoad } from './$types';
import { redirect } from '@sveltejs/kit';

export const load: PageServerLoad = async (event) => {
  // handle でセットした locals.user をここで参照できる
    const user = event.locals.user;

  if (!user) {
    redirect(303, '/login');
  }

  return { user };
};

🎯 ここで理解できること

handle locals load という流れで、認証情報を一度取得すればすべてのページで使い回せます。第2回ではevent.cookies を使った本格的なセッション検証にこの仕組みを組み込みます。


resolve() のオプション ― HTMLを変換する

resolve(event) には第2引数としてオプションを渡せます。中でもtransformPageChunk は、サーバーが生成したHTMLを送信前に書き換えられる強力なオプションです。

ts
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
  event.locals.user = null;

  return resolve(event, {
    // HTML の <html> タグにテーマクラスを動的に追加する例
        transformPageChunk: ({ html }) =>
            html.replace('<html lang="ja">', '<html lang="ja" data-theme="light">'),
  });
};
オプション 用途
transformPageChunk 生成されたHTMLを送信前に変換する。テーマ適用・OGタグの動的挿入など
filterSerializedResponseHeaders クライアントに渡すレスポンスヘッダーを絞り込む
preload プリロードするリソース(CSS・JS・フォントなど)を制御する

hooks.ts ― reroute でURLを正規化する

🔄 サーバー+ブラウザ両方で実行

hooks.ts (universal)で使えるreroute は、 URLをルーターが解析する前に書き換える フックです。リクエストのURLを変えずに、内部的に別のルートを使いたいときに活用します。

商品カタログアプリで実用的な例を実装します。/shop というURLへのアクセスを内部的に/products へ転送します。ブラウザのURLバーは/shop のまま変わりません。

✨ redirect との違い

redirect() はブラウザのURLも変わります(例:/shop /products に変わる)。reroute はURLを変えずに内部的にルートを切り替えるため、URLエイリアスのような用途に使います。

ts
import type { Reroute } from '@sveltejs/kit';

// URLエイリアスの定義
const aliases: Record<string, string> = {
  '/shop':         '/products',
  '/item':         '/products',
  '/my':           '/mypage',
};

export const reroute: Reroute = ({ url }) => {
  // aliases に一致するパスがあれば内部パスを返す
    if (url.pathname in aliases) {
      return aliases[url.pathname];
    }
  // 一致しなければそのままのパスを返す(undefined でも可)
  return url.pathname;
};

🎯 確認

http://localhost:5173/shop にアクセスして商品一覧ページが表示されることを確認してください。URLバーは/shop のままです。http://localhost:5173/my でマイページも同様に確認できます。


第1回の完成コード一覧

ts
src/
 ├─ hooks.server.ts NEW ← handle・locals のセット
 ├─ hooks.ts NEW ← reroute によるURLエイリアス
 ├─ app.d.ts UPDATE ← App.Locals に user を追加
 └─ routes/(shop)/mypage/
                  └─ +page.server.ts UPDATE ← locals.user を参照

📄

src/app.d.ts

App.Locals の型定義

ts
declare global {
  namespace App {
    interface Locals {
      user: { id: string; name: string } | null;
    }
  }
}
export {};

📄

src/hooks.server.ts

handle・locals・レスポンスヘッダー

ts
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
  // リクエストのログ(第2回でより詳細なロギングを追加)
  console.log(`[handle] ${event.request.method} ${event.url.pathname}`);

  // locals にユーザー情報をセット(第2回で Cookie 検証に差し替える)
  event.locals.user = null;

  const response = await resolve(event, {
    // HTML の <html> タグにデフォルトテーマを付与
    transformPageChunk: ({ html }) =>
      html.replace('<html lang="ja">', '<html lang="ja" data-theme="light">'),
  });

  // セキュリティヘッダーを追加
  response.headers.set('X-Frame-Options', 'SAMEORIGIN');

  return response;
};

📄

src/hooks.ts

reroute によるURLエイリアス

ts
import type { Reroute } from '@sveltejs/kit';

const aliases: Record<string, string> = {
  '/shop': '/products',
  '/item': '/products',
  '/my':   '/mypage',
};

export const reroute: Reroute = ({ url }) => {
  if (url.pathname in aliases) return aliases[url.pathname];
  return url.pathname;
};

📄

src/routes/(shop)/mypage/+page.server.ts

locals.user を参照(第2回で本格認証に差し替え)

ts
declare global {
  namespace App {
    interface Locals {
      user: { id: string; name: string } | null;
    }
  }
}
export {};

第1回のまとめ

今回学んだこと

  • ✔️ Hooks はload 関数とは異なり、 すべてのリクエストに横断的に割り込む 仕組み
  • ✔️hooks.server.ts (サーバー専用)・hooks.client.ts (ブラウザ専用)・hooks.ts (universal)の3ファイルが存在し、使えるフック関数がそれぞれ異なる
  • ✔️handle 関数はevent resolve を受け取る。resolve(event) を呼ぶことで通常のページ処理が走る
  • ✔️event.locals handle load →ページへデータを渡すための入れ物。src/app.d.ts で型を定義する
  • ✔️resolve(event, options) transformPageChunk で生成済みHTMLを変換できる
  • ✔️hooks.ts reroute でURLを変えずに内部ルートを切り替えるエイリアスを作れる

🎯 第1回のまとめ

Hooks の仕組みと3ファイルの役割を把握しました。今回はlocals.user をダミーの固定値でセットしていますが、次回はいよいよevent.cookies を使ったセッション管理とログインフォーム(Form Actions)の実装を行い、 認証を完全に Hooks で一元化 します。