Skip to content

Files

This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Latest commit

aed1edc · Jan 18, 2022

History

History
219 lines (156 loc) · 8.4 KB

01-routing.md

File metadata and controls

219 lines (156 loc) · 8.4 KB
title
Routing

At the heart of SvelteKit is a filesystem-based router. This means that the structure of your application is defined by the structure of your codebase — specifically, the contents of src/routes.

You can change this to a different directory by editing the project config.

There are two types of route — pages and endpoints.

Pages typically generate HTML to display to the user (as well as any CSS and JavaScript needed for the page). By default, pages are rendered on both the client and server, though this behaviour is configurable.

Endpoints run only on the server (or when you build your site, if prerendering). This means it's the place to do things like access databases or APIs that require private credentials or return data that lives on a machine in your production network. Pages can request data from endpoints. Endpoints return JSON by default, though may also return data in other formats.

Pages

Pages are Svelte components written in .svelte files (or any file with an extension listed in config.extensions). By default, when a user first visits the application, they will be served a server-rendered version of the page in question, plus some JavaScript that 'hydrates' the page and initialises a client-side router. From that point forward, navigating to other pages is handled entirely on the client for a fast, app-like feel where the common portions in the layout do not need to be rerendered.

The filename determines the route. For example, src/routes/index.svelte is the root of your site:

<!-- src/routes/index.svelte -->
<svelte:head>
	<title>Welcome</title>
</svelte:head>

<h1>Hello and welcome to my site!</h1>

A file called either src/routes/about.svelte or src/routes/about/index.svelte would correspond to the /about route:

<!-- src/routes/about.svelte -->
<svelte:head>
	<title>About</title>
</svelte:head>

<h1>About this site</h1>
<p>TODO...</p>

Dynamic parameters are encoded using [brackets]. For example, a blog post might be defined by src/routes/blog/[slug].svelte. Soon, we'll see how to access that parameter in a load function or the page store.

A file or directory can have multiple dynamic parts, like [id]-[category].svelte. (Parameters are 'non-greedy'; in an ambiguous case like x-y-z, id would be x and category would be y-z.)

Endpoints

Endpoints are modules written in .js (or .ts) files that export functions corresponding to HTTP methods.

// Declaration types for Endpoints
// * declarations that are not exported are for internal use

// type of string[] is only for set-cookie
// everything else must be a type of string
type ResponseHeaders = Record<string, string | string[]>;

export interface RequestEvent<Locals = Record<string, any>> {
	request: Request;
	url: URL;
	params: Record<string, string>;
	locals: Locals;
}

type Body = JSONResponse | Uint8Array | string | ReadableStream | stream.Readable;

export interface EndpointOutput {
	status?: number;
	headers?: HeadersInit;
	body?: Body;
}

export type MaybePromise<T> = T | Promise<T>;

export interface Fallthrough {
	fallthrough?: true;
}

export interface RequestHandler<
	Locals = Record<string, any>,
	Input = unknown,
	Output extends DefaultBody = DefaultBody
> {
	(request: Request<Locals, Input>): MaybePromise<Fallthrough | EndpointOutput<Output>>;
}

For example, our hypothetical blog page, /blog/cool-article, might request data from /blog/cool-article.json, which could be represented by a src/routes/blog/[slug].json.js endpoint:

import db from '$lib/database';

/** @type {import('@sveltejs/kit').RequestHandler} */
export async function get({ params }) {
	// the `slug` parameter is available because this file
	// is called [slug].json.js
	const article = await db.get(params.slug);

	if (article) {
		return {
			body: {
				article
			}
		};
	}

	return {
		status: 404
	};
}

All server-side code, including endpoints, has access to fetch in case you need to request data from external APIs.

The job of this function is to return a { status, headers, body } object representing the response, where status is an HTTP status code:

  • 2xx — successful response (default is 200)
  • 3xx — redirection (should be accompanied by a location header)
  • 4xx — client error
  • 5xx — server error

If the returned body is an object, and no content-type header is returned, it will automatically be turned into a JSON response. (Don't worry about $lib, we'll get to that later.)

If {fallthrough: true} is returned SvelteKit will fall through to other routes until something responds, or will respond with a generic 404.

For endpoints that handle other HTTP methods, like POST, export the corresponding function:

export function post(request) {...}

Since delete is a reserved word in JavaScript, DELETE requests are handled with a del function.

We don't interact with the req/res objects you might be familiar with from Node's http module or frameworks like Express, because they're only available on certain platforms. Instead, SvelteKit translates the returned object into whatever's required by the platform you're deploying your app to.

To set multiple cookies in a single set of response headers, you can return an array:

return {
	headers: {
		'set-cookie': [cookie1, cookie2]
	}
};

Body parsing

The request object is an instance of the standard Request class. As such, accessing the request body is easy:

export async function post({ request }) {
	const data = await request.formData(); // or .json(), or .text(), etc
}

HTTP Method Overrides

HTML <form> elements only support GET and POST methods natively. You can allow other methods, like PUT and DELETE, by specifying them in your configuration and adding a _method=VERB parameter (you can configure the name) to the form's action:

// svelte.config.js
export default {
	kit: {
		methodOverride: {
			allowed: ['PUT', 'PATCH', 'DELETE']
		}
	}
};
<form method="post" action="/todos/{id}?_method=PUT">
	<!-- form elements -->
</form>

Using native <form> behaviour ensures your app continues to work when JavaScript fails or is disabled.

Private modules

A filename that has a segment with a leading underscore, such as src/routes/foo/_Private.svelte or src/routes/bar/_utils/cool-util.js, is hidden from the router, but can be imported by files that are not.

Advanced

Rest parameters

A route can have multiple dynamic parameters, for example src/routes/[category]/[item].svelte or even src/routes/[category]-[item].svelte. If the number of route segments is unknown, you can use rest syntax — for example you might implement GitHub's file viewer like so...

/[org]/[repo]/tree/[branch]/[...file]

...in which case a request for /sveltejs/kit/tree/master/documentation/docs/01-routing.md would result in the following parameters being available to the page:

{
	org: 'sveltejs',
	repo: 'kit',
	branch: 'master',
	file: 'documentation/docs/01-routing.md'
}

src/routes/a/[...rest]/z.svelte will match /a/z as well as /a/b/z and /a/b/c/z and so on. Make sure you check that the value of the rest parameter is valid.

Fallthrough routes

Finally, if you have multiple routes that match a given path, SvelteKit will try each of them until one responds. For example if you have these routes...

src/routes/[baz].js
src/routes/[baz].svelte
src/routes/[qux].svelte
src/routes/foo-[bar].svelte

... and you navigate to /foo-xyz, then SvelteKit will first try foo-[bar].svelte because it is the best match. If that yields no response, SvelteKit will try other less specific yet still valid matches for /foo-xyz. Since endpoints have higher precedence than pages, the next attempt will be [baz].js. Then alphabetical order takes precedence and thus [baz].svelte will be tried before [qux].svelte. The first route that responds — a page that returns something from load or has no load function, or an endpoint that returns something — will handle the request.

If no page or endpoint responds to a request, SvelteKit will respond with a generic 404.