A RetroSearch Logo

Home - News ( United States | United Kingdom | Italy | Germany ) - Football scores

Search Query:

Showing content from https://kit.svelte.dev/docs/hooks below:

Hooks • Docs • Svelte

‘Hooks’ are app-wide functions you declare that SvelteKit will call in response to specific events, giving you fine-grained control over the framework’s behaviour.

There are three hooks files, all optional:

Code in these modules will run when the application starts up, making them useful for initializing database clients and so on.

You can configure the location of these files with config.kit.files.hooks.

Server hooks

The following hooks can be added to src/hooks.server.js:

handle

This function runs every time the SvelteKit server receives a request — whether that happens while the app is running, or during prerendering — and determines the response. It receives an event object representing the request and a function called resolve, which renders the route and generates a Response. This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing routes programmatically, for example).

src/hooks.server

/** @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;
}
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
	if (event.url.pathname.startsWith('/custom')) {
		return new Response('custom response');
	}

	const response = await resolve(event);
	return response;
};

Requests for static assets — which includes pages that were already prerendered — are not handled by SvelteKit.

If unimplemented, defaults to ({ event, resolve }) => resolve(event).

During prerendering, SvelteKit crawls your pages for links and renders each route it finds. Rendering the route invokes the handle function (and all other route dependencies, like load). If you need to exclude some code from running during this phase, check that the app is not building beforehand.

locals

To add custom data to the request, which is passed to handlers in +server.js and server load functions, populate the event.locals object, as shown below.

src/hooks.server

/** @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);

	// Note that modifying response headers isn't always safe.
	// Response objects can have immutable headers
	// (e.g. Response.redirect() returned from an endpoint).
	// Modifying immutable headers throws a TypeError.
	// In that case, clone the response or avoid creating a
	// response object with immutable headers.
	response.headers.set('x-custom-header', 'potato');

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

export const handle: Handle = async ({ event, resolve }) => {
	event.locals.user = await getUserInformation(event.cookies.get('sessionid'));

	const response = await resolve(event);

	// Note that modifying response headers isn't always safe.
	// Response objects can have immutable headers
	// (e.g. Response.redirect() returned from an endpoint).
	// Modifying immutable headers throws a TypeError.
	// In that case, clone the response or avoid creating a
	// response object with immutable headers.
	response.headers.set('x-custom-header', 'potato');

	return response;
};

You can define multiple handle functions and execute them with the sequence helper function.

resolve also supports a second, optional parameter that gives you more control over how the response will be rendered. That parameter is an object that can have the following fields:

src/hooks.server

/** @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;
}
import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ 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;
};

Note that resolve(...) will never throw an error, it will always return a Promise<Response> with the appropriate status code. If an error is thrown elsewhere during handle, it is treated as fatal, and SvelteKit will respond with a JSON representation of the error or a fallback error page — which can be customised via src/error.html — depending on the Accept header. You can read more about error handling here.

handleFetch

This function allows you to modify (or replace) the result of an event.fetch call that runs on the server (or during prerendering) inside an endpoint, load, action, handle, handleError or reroute.

For example, your load function might make a request to a public URL like https://api.yourapp.com when the user performs a client-side navigation to the respective page, but during SSR it might make sense to hit the API directly (bypassing whatever proxies and load balancers sit between it and the public internet).

src/hooks.server

/** @type {import('@sveltejs/kit').HandleFetch} */
export async function handleFetch({ request, fetch }) {
	if (request.url.startsWith('https://api.yourapp.com/')) {
		// clone the original request, but change the URL
		request = new Request(
			request.url.replace('https://api.yourapp.com/', 'http://localhost:9999/'),
			request
		);
	}

	return fetch(request);
}
import type { HandleFetch } from '@sveltejs/kit';

export const handleFetch: HandleFetch = async ({ request, fetch }) => {
	if (request.url.startsWith('https://api.yourapp.com/')) {
		// clone the original request, but change the URL
		request = new Request(
			request.url.replace('https://api.yourapp.com/', 'http://localhost:9999/'),
			request
		);
	}

	return fetch(request);
};

Requests made with event.fetch follow the browser’s credentials model — for same-origin requests, cookie and authorization headers are forwarded unless the credentials option is set to "omit". For cross-origin requests, cookie will be included if the request URL belongs to a subdomain of the app — for example if your app is on my-domain.com, and your API is on api.my-domain.com, cookies will be included in the request.

There is one caveat: if your app and your API are on sibling subdomains — www.my-domain.com and api.my-domain.com for example — then a cookie belonging to a common parent domain like my-domain.com will not be included, because SvelteKit has no way to know which domain the cookie belongs to. In these cases you will need to manually include the cookie using handleFetch:

src/hooks.server

/** @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);
}
import type { HandleFetch } from '@sveltejs/kit';
export const handleFetch: HandleFetch = async ({ event, request, fetch }) => {
	if (request.url.startsWith('https://api.my-domain.com/')) {
		request.headers.set('cookie', event.request.headers.get('cookie'));
	}

	return fetch(request);
};
handleValidationError

This hook is called when a remote function is called with an argument that does not match the provided Standard Schema. It must return an object matching the shape of App.Error.

Say you have a remote function that expects a string as its argument ...

todos.remote

import * as v from 'valibot';
import { query } from '$app/server';

export const getTodo = query(v.string(), (id) => {
	// implementation...
});

...but it is called with something that doesn’t match the schema — such as a number (e.g await getTodos(1)) — then validation will fail, the server will respond with a 400 status code, and the function will throw with the message ‘Bad Request’.

To customise this message and add additional properties to the error object, implement handleValidationError:

src/hooks.server

/** @type {import('@sveltejs/kit').HandleValidationError} */
export function handleValidationError({ issues }) {
	return {
		message: 'No thank you'
	};
}
import type { HandleValidationError } from '@sveltejs/kit';

export const handleValidationError: HandleValidationError = ({ issues }) => {
	return {
		message: 'No thank you'
	};
};

Be thoughtful about what information you expose here, as the most likely reason for validation to fail is that someone is sending malicious requests to your server.

The following can be added to src/hooks.server.js and src/hooks.client.js:

handleError

If an unexpected error is thrown during loading, rendering, or from an endpoint, this function will be called with the error, event, status code and message. This allows for two things:

For errors thrown from your code (or library code called by your code) the status will be 500 and the message will be “Internal Error”. While error.message may contain sensitive information that should not be exposed to users, message is safe (albeit meaningless to the average user).

To add more information to the $page.error object in a type-safe way, you can customize the expected shape by declaring an App.Error interface (which must include message: string, to guarantee sensible fallback behavior). This allows you to — for example — append a tracking ID for users to quote in correspondence with your technical support staff:

src/app.d

declare global {
	namespace App {
		interface Error {
			message: string;
			errorId: string;
		}
	}
}

export {};

src/hooks.server

import * as Sentry from '@sentry/sveltekit';

Sentry.init({/*...*/})

/** @type {import('@sveltejs/kit').HandleServerError} */
export async function handleError({ error, event, status, message }) {
	const errorId = crypto.randomUUID();

	// example integration with https://sentry.io/
	Sentry.captureException(error, {
		extra: { event, errorId, status }
	});

	return {
		message: 'Whoops!',
		errorId
	};
}
import * as Sentry from '@sentry/sveltekit';
import type { HandleServerError } from '@sveltejs/kit';

Sentry.init({/*...*/})

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

	// example integration with https://sentry.io/
	Sentry.captureException(error, {
		extra: { event, errorId, status }
	});

	return {
		message: 'Whoops!',
		errorId
	};
};

src/hooks.client

import * as Sentry from '@sentry/sveltekit';

Sentry.init({/*...*/})

/** @type {import('@sveltejs/kit').HandleClientError} */
export async function handleError({ error, event, status, message }) {
	const errorId = crypto.randomUUID();

	// example integration with https://sentry.io/
	Sentry.captureException(error, {
		extra: { event, errorId, status }
	});

	return {
		message: 'Whoops!',
		errorId
	};
}
import * as Sentry from '@sentry/sveltekit';
import type { HandleClientError } from '@sveltejs/kit';

Sentry.init({/*...*/})

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

	// example integration with https://sentry.io/
	Sentry.captureException(error, {
		extra: { event, errorId, status }
	});

	return {
		message: 'Whoops!',
		errorId
	};
};

In src/hooks.client.js, the type of handleError is HandleClientError instead of HandleServerError, and event is a NavigationEvent rather than a RequestEvent.

This function is not called for expected errors (those thrown with the error function imported from @sveltejs/kit).

During development, if an error occurs because of a syntax error in your Svelte code, the passed in error has a frame property appended highlighting the location of the error.

Make sure that handleError never throws an error

init

This function runs once, when the server is created or the app starts in the browser, and is a useful place to do asynchronous work such as initializing a database connection.

If your environment supports top-level await, the init function is really no different from writing your initialisation logic at the top level of the module, but some environments — most notably, Safari — don’t.

src/hooks.server

import * as db from '$lib/server/database';

/** @type {import('@sveltejs/kit').ServerInit} */
export async function init() {
	await db.connect();
}
import * as db from '$lib/server/database';
import type { ServerInit } from '@sveltejs/kit';

export const init: ServerInit = async () => {
	await db.connect();
};

In the browser, asynchronous work in init will delay hydration, so be mindful of what you put in there.

Universal hooks

The following can be added to src/hooks.js. Universal hooks run on both server and client (not to be confused with shared hooks, which are environment-specific).

reroute

This function runs before handle and allows you to change how URLs are translated into routes. The returned pathname (which defaults to url.pathname) is used to select the route and its parameters.

For example, you might have a src/routes/[[lang]]/about/+page.svelte page, which should be accessible as /en/about or /de/ueber-uns or /fr/a-propos. You could implement this with reroute:

src/hooks


/** @type {Record<string, string>} */
const translated = {
	'/en/about': '/en/about',
	'/de/ueber-uns': '/de/about',
	'/fr/a-propos': '/fr/about',
};

/** @type {import('@sveltejs/kit').Reroute} */
export function reroute({ url }) {
	if (url.pathname in translated) {
		return translated[url.pathname];
	}
}
import type { Reroute } from '@sveltejs/kit';
const translated: Record<string, string> = {
	'/en/about': '/en/about',
	'/de/ueber-uns': '/de/about',
	'/fr/a-propos': '/fr/about',
};

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

The lang parameter will be correctly derived from the returned pathname.

Using reroute will not change the contents of the browser’s address bar, or the value of event.url.

Since version 2.18, the reroute hook can be asynchronous, allowing it to (for example) fetch data from your backend to decide where to reroute to. Use this carefully and make sure it’s fast, as it will delay navigation otherwise. If you need to fetch data, use the fetch provided as an argument. It has the same benefits as the fetch provided to load functions, with the caveat that params and id are unavailable to handleFetch because the route is not yet known.

src/hooks


/** @type {import('@sveltejs/kit').Reroute} */
export async function reroute({ url, fetch }) {
	// Ask a special endpoint within your app about the destination
	if (url.pathname === '/api/reroute') return;

	const api = new URL('/api/reroute', url);
	api.searchParams.set('pathname', url.pathname);

	const result = await fetch(api).then(r => r.json());
	return result.pathname;
}
import type { Reroute } from '@sveltejs/kit';
export const reroute: Reroute = async ({ url, fetch }) => {
	// Ask a special endpoint within your app about the destination
	if (url.pathname === '/api/reroute') return;

	const api = new URL('/api/reroute', url);
	api.searchParams.set('pathname', url.pathname);

	const result = await fetch(api).then(r => r.json());
	return result.pathname;
};

reroute is considered a pure, idempotent function. As such, it must always return the same output for the same input and not have side effects. Under these assumptions, SvelteKit caches the result of reroute on the client so it is only called once per unique URL.

transport

This is a collection of transporters, which allow you to pass custom types — returned from load and form actions — across the server/client boundary. Each transporter contains an encode function, which encodes values on the server (or returns a falsy value for anything that isn’t an instance of the type) and a corresponding decode function:

src/hooks

import { Vector } from '$lib/math';

/** @type {import('@sveltejs/kit').Transport} */
export const transport = {
	Vector: {
		encode: (value) => value instanceof Vector && [value.x, value.y],
		decode: ([x, y]) => new Vector(x, y)
	}
};
import { Vector } from '$lib/math';
import type { Transport } from '@sveltejs/kit';

export const transport: Transport = {
	Vector: {
		encode: (value) => value instanceof Vector && [value.x, value.y],
		decode: ([x, y]) => new Vector(x, y)
	}
};
Further reading

RetroSearch is an open source project built by @garambo | Open a GitHub Issue

Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo

HTML: 3.2 | Encoding: UTF-8 | Version: 0.7.4