
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 サーバー・クライアント動作入門
前シリーズでは商品カタログアプリを通じて、ルーティング・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が自動的に認識します。存在しない場合は単にスキップされるので、必要なファイルだけ作れば問題ありません。
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 ) を受け取ります。
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 に型定義を追加します。
// 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 にセットする
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 を受け取る
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を送信前に書き換えられる強力なオプションです。
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エイリアスのような用途に使います。
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回の完成コード一覧
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 の型定義
declare global {
namespace App {
interface Locals {
user: { id: string; name: string } | null;
}
}
}
export {};📄
src/hooks.server.ts
handle・locals・レスポンスヘッダー
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エイリアス
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回で本格認証に差し替え)
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 で一元化 します。
