Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

replace setup with hooks #670

Merged
merged 10 commits into from
Mar 25, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/young-beds-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

Replace setup with hooks
17 changes: 10 additions & 7 deletions documentation/docs/01-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,24 +39,29 @@ Dynamic parameters are encoded using `[brackets]`. For example, a blog post migh

### Endpoints

Endpoints are modules written in `.js` (or `.ts`) files that export functions corresponding to HTTP methods. Each function receives HTTP `request` and `context` objects as arguments. 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:
Endpoints are modules written in `.js` (or `.ts`) files that export functions corresponding to HTTP methods. 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:

```ts
type Request = {
type Request<Context = any> = {
host: string;
method: 'GET';
headers: Record<string, string>;
path: string;
params: Record<string, string | string[]>;
query: URLSearchParams;
body: string | Buffer | ReadOnlyFormData;
context: Context; // see getContext, below
};

type Response = {
status?: number;
headers?: Record<string, string>;
body?: any;
};

type RequestHandler<Context = any> = {
(request: Request<Context>) => Response | Promise<Response>;
}
```

```js
Expand All @@ -65,10 +70,10 @@ import db from '$lib/database';
/**
* @type {import('@sveltejs/kit').RequestHandler}
*/
export async function get(request, context) {
export async function get({ params }) {
// the `slug` parameter is available because this file
// is called [slug].json.js
const { slug } = request.params;
const { slug } = params;

const article = await db.get(slug);

Expand All @@ -86,14 +91,12 @@ export async function get(request, context) {

Because this module only runs on the server (or when you build your site, if [prerendering](#prerendering)), you can freely access things like databases. (Don't worry about `$lib`, we'll get to that [later](#$lib).)

The second argument, `context`, is something you define during [setup](#setup), if necessary.

The job of this function is to return a `{status, headers, body}` object representing the response. If the returned `body` is an object, and no `content-type` header is returned, it will automatically be turned into a JSON response.

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

```js
export function post(request, context) {...}
export function post(request) {...}
```

Since `delete` is a reserved word in JavaScript, DELETE requests are handled with a `del` function.
Expand Down
2 changes: 1 addition & 1 deletion documentation/docs/03-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ So if the example above was `src/routes/blog/[slug].svelte` and the URL was `/bl

#### session

`session` can be used to pass data from the server related to the current request, e.g. the current user. By default it is `undefined`. See [Setup](#setup) to learn how to use it.
`session` can be used to pass data from the server related to the current request, e.g. the current user. By default it is `undefined`. See [`getSession`](#hooks-getsession) to learn how to use it.

#### context

Expand Down
117 changes: 117 additions & 0 deletions documentation/docs/04-hooks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
---
title: Hooks
---

An optional `src/hooks.js` (or `src/hooks.ts`, or `src/hooks/index.js`) file exports three functions, all optional, that run on the server — **getContext**, **getSession** and **handle**.

> The location of this file can be [configured](#configuration) as `config.kit.files.hooks`

### getContext

This function runs on every incoming request, and generates the `context` property passed to [endpoint handlers](#routing-endpoints) and used to derive the [`session`](#hooks-getsession) object available in the browser.
Rich-Harris marked this conversation as resolved.
Show resolved Hide resolved

If unimplemented, context is `{}`.

```ts
type Incoming = {
Rich-Harris marked this conversation as resolved.
Show resolved Hide resolved
method: string;
host: string;
headers: Headers;
path: string;
query: URLSearchParams;
body: string | Buffer | ReadOnlyFormData;
};

type GetContext<Context = any> = {
(incoming: Incoming): Context;
};
```

```js
import * as cookie from 'cookie';
import db from '$lib/db';

/** @type {import('@sveltejs/kit').GetContext} */
export async function getContext({ headers }) {
const cookies = cookie.parse(headers.cookie || '');

return {
user: (await db.get_user(cookies.session_id)) || { guest: true }
};
}
```

### getSession

This function takes the [`context`](#hooks-getcontext) object and returns a `session` object that is safe to expose to the browser. It runs whenever SvelteKit renders a page.

If unimplemented, session is `{}`.

```ts
type GetSession<Context = any, Session = any> = {
({ context }: { context: Context }): Session;
};
```

```js
/** @type {import('@sveltejs/kit').GetSession} */
export function getSession({ context }) {
Rich-Harris marked this conversation as resolved.
Show resolved Hide resolved
return {
user: {
// only include properties needed client-side —
// exclude anything else attached to the user
// like access tokens etc
name: context.user?.name,
email: context.user?.email,
avatar: context.user?.avatar
}
};
}
```

> `session` must be serializable, which means it must not contain things like functions or custom classes, just built-in JavaScript data types

### handle

This function runs on every request, and determines the response. The second argument, `render`, calls SvelteKit's default renderer. This allows you to modify response headers or bodies, or bypass SvelteKit entirely (for implementing endpoints programmatically, for example).

If unimplemented, defaults to `(request, render) => render(request)`.

```ts
type Request<Context = any> = {
method: string;
host: string;
headers: Headers;
path: string;
params: Record<string, string>;
query: URLSearchParams;
body: string | Buffer | ReadOnlyFormData;
context: Context;
};

type Response = {
status?: number;
headers?: Headers;
body?: any;
};

type Handle<Context = any> = (
request: Request<Context>,
render: (request: Request<Context>) => Response | Promise<Response>
) => Response | Promise<Response>;
```

```js
/** @type {import('@sveltejs/kit').Handle} */
export async function handle(request, render) {
const response = await render(request);

return {
...response,
headers: {
...response.headers,
'x-custom-header': 'potato'
}
};
}
```
104 changes: 0 additions & 104 deletions documentation/docs/04-setup.md

This file was deleted.

31 changes: 15 additions & 16 deletions documentation/docs/05-modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,28 @@ SvelteKit makes a number of modules available to your application.
import { amp, browser, dev } from '$app/env';
```

* `amp` is `true` or `false` depending on the corresponding value in your [project configuration](#configuration)
* `browser` is `true` or `false` depending on whether the app is running in the browser or on the server
* `dev` is `true` in development mode, `false` in production
- `amp` is `true` or `false` depending on the corresponding value in your [project configuration](#configuration)
- `browser` is `true` or `false` depending on whether the app is running in the browser or on the server
- `dev` is `true` in development mode, `false` in production

### $app/navigation

```js
import { goto, prefetch, prefetchRoutes } from '$app/navigation';
```

* `goto(href, { replaceState, noscroll })` returns a `Promise` that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) to the specified `href`. The second argument is optional. If `replaceState` is true, a new history entry won't be created. If `noscroll` is true, the browser won't scroll to the top of the page after navigation.
* `prefetch(href)` programmatically prefetches the given page, which means a) ensuring that the code for the page is loaded, and b) calling the page's `load` function with the appropriate options. This is the same behaviour that SvelteKit triggers when the user taps or mouses over an `<a>` element with [sveltekit:prefetch](docs#anchor-options-sveltekit-prefetch). If the next navigation is to `href`, the values returned from `load` will be used, making navigation instantaneous. Returns a `Promise` that resolves when the prefetch is complete.
* `prefetchRoutes(routes)` — programmatically prefetches the code for routes that haven't yet been fetched. Typically, you might call this to speed up subsequent navigation. If no argument is given, all routes will be fetched, otherwise you can specify routes by any matching pathname such as `/about` (to match `src/routes/about.svelte`) or `/blog/*` (to match `src/routes/blog/[slug].svelte`). Unlike `prefetch`, this won't call `preload` for individual pages. Returns a `Promise` that resolves when the routes have been prefetched.
- `goto(href, { replaceState, noscroll })` returns a `Promise` that resolves when SvelteKit navigates (or fails to navigate, in which case the promise rejects) to the specified `href`. The second argument is optional. If `replaceState` is true, a new history entry won't be created. If `noscroll` is true, the browser won't scroll to the top of the page after navigation.
- `prefetch(href)` programmatically prefetches the given page, which means a) ensuring that the code for the page is loaded, and b) calling the page's `load` function with the appropriate options. This is the same behaviour that SvelteKit triggers when the user taps or mouses over an `<a>` element with [sveltekit:prefetch](docs#anchor-options-sveltekit-prefetch). If the next navigation is to `href`, the values returned from `load` will be used, making navigation instantaneous. Returns a `Promise` that resolves when the prefetch is complete.
- `prefetchRoutes(routes)` — programmatically prefetches the code for routes that haven't yet been fetched. Typically, you might call this to speed up subsequent navigation. If no argument is given, all routes will be fetched, otherwise you can specify routes by any matching pathname such as `/about` (to match `src/routes/about.svelte`) or `/blog/*` (to match `src/routes/blog/[slug].svelte`). Unlike `prefetch`, this won't call `preload` for individual pages. Returns a `Promise` that resolves when the routes have been prefetched.

### $app/paths

```js
import { base, assets } from '$app/paths';
```

* `base` — a root-relative (i.e. begins with a `/`) string that matches `config.kit.files.base` in your [project configuration](#configuration)
* `assets` — a root-relative or absolute path that matches `config.kit.files.assets` (after it has been resolved against `base`)
- `base` — a root-relative (i.e. begins with a `/`) string that matches `config.kit.files.base` in your [project configuration](#configuration)
- `assets` — a root-relative or absolute path that matches `config.kit.files.assets` (after it has been resolved against `base`)

### $app/stores

Expand All @@ -43,14 +43,13 @@ Stores are _contextual_ — they are added to the [context](https://svelte.dev/t

Because of that, the stores are not free-floating objects: they must be accessed during component initialisation, like anything else that would be accessed with `getContext`.

* `getStores` is a convenience function around `getContext` that returns `{ navigating, page, session }`. Most of the time, you won't need to use it.
- `getStores` is a convenience function around `getContext` that returns `{ navigating, page, session }`. Most of the time, you won't need to use it.

The stores themselves attach to the correct context at the point of subscription, which means you can import and use them directly in components without boilerplate.

* `navigating` is a [readable store](https://svelte.dev/tutorial/readable-stores). When navigating starts, its value is `{ from, to }`, where `from` and `to` both mirror the `page` store value. When navigating finishes, its value reverts to `null`.
* `page` is a readable store whose value reflects the object passed to `load` functions — it contains `host`, `path`, `params` and `query`
* `session` is a [writable store](https://svelte.dev/tutorial/writable-stores) whose initial value is whatever was returned from [`getSession`](#setup-getsession). It can be written to, but this will _not_ cause changes to persist on the server — this is something you must implement yourself.

- `navigating` is a [readable store](https://svelte.dev/tutorial/readable-stores). When navigating starts, its value is `{ from, to }`, where `from` and `to` both mirror the `page` store value. When navigating finishes, its value reverts to `null`.
- `page` is a readable store whose value reflects the object passed to `load` functions — it contains `host`, `path`, `params` and `query`
- `session` is a [writable store](https://svelte.dev/tutorial/writable-stores) whose initial value is whatever was returned from [`getSession`](#hooks-getsession). It can be written to, but this will _not_ cause changes to persist on the server — this is something you must implement yourself.

### $lib

Expand All @@ -64,6 +63,6 @@ This module is only available to [service workers](#service-workers).
import { build, files, timestamp } from '$service-worker';
```

* `build` is an array of URL strings representing the files generated by Vite, suitable for caching with `cache.addAll(build)`
* `files` is an array of URL strings representing the files in your `static` directory, or whatever directory is specified by [`config.kit.files.assets`](#configuration)
* `timestamp` is the result of calling `Date.now()` at build time. It's useful for generating unique cache names inside your service worker, so that a later deployment of your app can invalidate old caches
- `build` is an array of URL strings representing the files generated by Vite, suitable for caching with `cache.addAll(build)`
- `files` is an array of URL strings representing the files in your `static` directory, or whatever directory is specified by [`config.kit.files.assets`](#configuration)
- `timestamp` is the result of calling `Date.now()` at build time. It's useful for generating unique cache names inside your service worker, so that a later deployment of your app can invalidate old caches
4 changes: 2 additions & 2 deletions documentation/docs/13-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ module.exports = {
appDir: '_app',
files: {
assets: 'static',
hooks: 'src/hooks',
lib: 'src/lib',
routes: 'src/routes',
serviceWorker: 'src/service-worker',
setup: 'src/setup',
template: 'src/app.html'
},
host: null,
Expand Down Expand Up @@ -66,7 +66,7 @@ An object containing zero or more of the following `string` values:
- `lib` — your app's internal library, accessible throughout the codebase as `$lib`
- `routes` — the files that define the structure of your app (see [Routing](#routing))
- `serviceWorker` — the location of your service worker's entry point (see [Service workers](#service-workers))
- `setup` — the location of your setup file (see [Setup](#setup))
- `hooks` — the location of your hooks module (see [Hooks](#hooks))
- `template` — the location of the template for HTML responses

#### host
Expand Down
Loading