Harness-engineering svelte-server-hooks

Svelte Server Hooks

install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/svelte-server-hooks" ~/.claude/skills/intense-visions-harness-engineering-svelte-server-hooks-92ebdc && rm -rf "$T"
manifest: agents/skills/codex/svelte-server-hooks/SKILL.md
source content

Svelte Server Hooks

Intercept every request, populate locals, modify responses, and handle errors using SvelteKit's hooks.server.ts

When to Use

  • You need to validate session cookies and attach a user object to every request
  • You want to add CORS headers, rate limiting, or logging across all routes
  • You need to modify outgoing responses (add security headers, compress, cache)
  • You are composing multiple
    handle
    functions using
    sequence
  • You want to capture and report unhandled server errors

Instructions

handle — the main request hook:

  1. Export a
    handle
    function from
    src/hooks.server.ts
    . It intercepts every request and must call
    resolve(event)
    to continue:
// src/hooks.server.ts
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
  // Runs before load functions and routes
  const response = await resolve(event);
  // Runs after
  return response;
};
  1. Attach per-request data to
    event.locals
    — accessible in all load functions and actions:
export const handle: Handle = async ({ event, resolve }) => {
  const sessionId = event.cookies.get('session_id');

  if (sessionId) {
    event.locals.user = await db.session.findUser(sessionId);
  }

  return resolve(event);
};
// src/app.d.ts — declare the locals shape
declare global {
  namespace App {
    interface Locals {
      user: User | null;
    }
  }
}
export {};
  1. Short-circuit a request (bypass routing) by returning a response directly:
export const handle: Handle = async ({ event, resolve }) => {
  if (event.url.pathname.startsWith('/api') && !event.locals.user) {
    return new Response('Unauthorized', { status: 401 });
  }
  return resolve(event);
};
  1. Modify the resolved response to add security headers:
export const handle: Handle = async ({ event, resolve }) => {
  const response = await resolve(event);
  response.headers.set('X-Frame-Options', 'DENY');
  response.headers.set('X-Content-Type-Options', 'nosniff');
  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
  return response;
};
  1. Use
    transformPageChunk
    to modify HTML output (inject scripts, replace placeholders):
const response = await resolve(event, {
  transformPageChunk: ({ html }) => html.replace('%theme%', event.locals.theme ?? 'light'),
});

sequence — composing multiple handle functions:

  1. Use
    sequence
    from
    @sveltejs/kit/hooks
    to chain multiple handle functions cleanly:
import { sequence } from '@sveltejs/kit/hooks';
import { auth } from './hooks/auth';
import { logging } from './hooks/logging';
import { security } from './hooks/security';

export const handle = sequence(auth, logging, security);

Each function in the sequence calls

resolve(event)
to pass to the next.

handleFetch — modifying server-side fetch calls:

  1. Intercept
    fetch
    calls made inside load functions (only on the server during SSR):
import type { HandleFetch } from '@sveltejs/kit';

export const handleFetch: HandleFetch = async ({ request, fetch }) => {
  // Rewrite internal API calls to avoid the network round-trip
  if (request.url.startsWith('https://myapp.com/api/')) {
    const url = request.url.replace('https://myapp.com', 'http://localhost:3000');
    return fetch(new Request(url, request));
  }
  return fetch(request);
};

handleError — unhandled server errors:

  1. Capture and report unexpected errors. The return value shapes what
    $page.error
    contains:
import type { HandleServerError } from '@sveltejs/kit';

export const handleError: HandleServerError = async ({ error, event, status, message }) => {
  const errorId = crypto.randomUUID();

  // Log to your error tracking service
  await reportError({ error, errorId, url: event.url.pathname });

  // Return safe error info (never expose internals)
  return {
    message: 'An unexpected error occurred',
    errorId,
  };
};

Client hooks (hooks.client.ts):

  1. Export
    handleError
    from
    src/hooks.client.ts
    to capture client-side errors:
// src/hooks.client.ts
import type { HandleClientError } from '@sveltejs/kit';

export const handleError: HandleClientError = ({ error, event }) => {
  Sentry.captureException(error);
  return { message: 'Something went wrong' };
};

Details

Execution order:

Request
  → handle (hooks.server.ts)
    → layout load (+layout.server.ts)
      → page load (+page.server.ts)
        → page renders
      ← response
    ← response
  ← handle (can modify)
Response

locals type safety:

Always declare your

App.Locals
interface in
src/app.d.ts
. TypeScript will then infer the correct types when you access
event.locals
in load functions and actions.

Auth pattern:

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

export const auth: Handle = async ({ event, resolve }) => {
  const token = event.cookies.get('auth_token');
  event.locals.user = token ? await verifyToken(token) : null;
  return resolve(event);
};

// Any load function:
export const load: PageServerLoad = ({ locals }) => {
  if (!locals.user) redirect(303, '/login');
  return { user: locals.user };
};

CORS for API routes:

export const handle: Handle = async ({ event, resolve }) => {
  if (event.request.method === 'OPTIONS') {
    return new Response(null, {
      status: 204,
      headers: {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET,POST,PUT,DELETE,OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type, Authorization',
      },
    });
  }

  const response = await resolve(event);
  response.headers.set('Access-Control-Allow-Origin', '*');
  return response;
};

handleFetch use cases:

  • Replace public API URLs with internal service URLs during SSR (avoid DNS round-trip)
  • Inject auth headers into all server-side fetch calls
  • Mock API calls in test environments

Source

https://kit.svelte.dev/docs/hooks

Process

  1. Read the instructions and examples in this document.
  2. Apply the patterns to your implementation, adapting to your specific context.
  3. Verify your implementation against the details and edge cases listed above.

Harness Integration

  • Type: knowledge — this skill is a reference document, not a procedural workflow.
  • No tools or state — consumed as context by other skills and agents.

Success Criteria

  • The patterns described in this document are applied correctly in the implementation.
  • Edge cases and anti-patterns listed in this document are avoided.