Skip to content

Commit

Permalink
docs: Elaborate on credentialed fetch behaviour (#10421)
Browse files Browse the repository at this point in the history
  • Loading branch information
lachlancollins committed Jul 29, 2023
1 parent dcf981c commit 6f36aef
Show file tree
Hide file tree
Showing 5 changed files with 39 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .changeset/orange-roses-destroy.md
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

docs: Elaborate on credentialed `fetch` behaviour
25 changes: 18 additions & 7 deletions documentation/docs/20-core-concepts/20-load.md
Expand Up @@ -231,10 +231,11 @@ Given a `route.id` of `/a/[b]/[...c]` and a `url.pathname` of `/a/x/y/z`, the `p

To get data from an external API or a `+server.js` handler, you can use the provided `fetch` function, which behaves identically to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch) with a few additional features:

- it can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request
- it can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context)
- internal requests (e.g. for `+server.js` routes) go direct to the handler function when running on the server, without the overhead of an HTTP call
- during server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](hooks#server-hooks-handle). Then, during hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request - if you got a warning in your browser console when using the browser `fetch` instead of the `load` `fetch`, this is why.
- It can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request.
- It can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context).
- Internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call.
- During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](hooks#server-hooks-handle).
- During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request - if you received a warning in your browser console when using the browser `fetch` instead of the `load` `fetch`, this is why.

```js
/// file: src/routes/items/[id]/+page.js
Expand All @@ -247,9 +248,7 @@ export async function load({ fetch, params }) {
}
```

> Cookies will only be passed through if the target host is the same as the SvelteKit application or a more specific subdomain of it.
## Cookies and headers
## Cookies

A server `load` function can get and set [`cookies`](types#public-types-cookies).

Expand All @@ -274,8 +273,20 @@ export async function load({ cookies }) {
}
```

Cookies will only be passed through the provided `fetch` function if the target host is the same as the SvelteKit application or a more specific subdomain of it.

For example, if SvelteKit is serving my.domain.com:
- domain.com WILL NOT receive cookies
- my.domain.com WILL receive cookies
- api.domain.dom WILL NOT receive cookies
- sub.my.domain.com WILL receive cookies

Other cookies will not be passed when `credentials: 'include'` is set, because SvelteKit does not know which domain which cookie belongs to (the browser does not pass this information along), so it's not safe to forward any of them. Use the [handleFetch hook](hooks#server-hooks-handlefetch) to work around it.

> When setting cookies, be aware of the `path` property. By default, the `path` of a cookie is the current pathname. If you for example set a cookie at page `admin/user`, the cookie will only be available within the `admin` pages by default. In most cases you likely want to set `path` to `'/'` to make the cookie available throughout your app.
## Headers

Both server and universal `load` functions have access to a `setHeaders` function that, when running on the server, can set headers for the response. (When running in the browser, `setHeaders` has no effect.) This is useful if you want the page to be cached, for example:

```js
Expand Down
2 changes: 1 addition & 1 deletion documentation/docs/20-core-concepts/50-state-management.md
Expand Up @@ -36,7 +36,7 @@ export const actions = {

The `user` variable is shared by everyone who connects to this server. If Alice submitted an embarrassing secret, and Bob visited the page after her, Bob would know Alice's secret. In addition, when Alice returns to the site later in the day, the server may have restarted, losing her data.

Instead, you should _authenticate_ the user using [`cookies`](load#cookies-and-headers) and persist the data to a database.
Instead, you should _authenticate_ the user using [`cookies`](load#cookies) and persist the data to a database.

## No side-effects in load

Expand Down
22 changes: 12 additions & 10 deletions packages/kit/src/exports/public.d.ts
Expand Up @@ -697,13 +697,13 @@ export interface LoadEvent<
/**
* `fetch` is equivalent to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch), with a few additional features:
*
* - it can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request
* - it can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context)
* - internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call
* - during server-side rendering, the response will be captured and inlined into the rendered HTML. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://kit.svelte.dev/docs/hooks#server-hooks-handle)
* - during hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request
* - It can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request.
* - It can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context).
* - Internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call.
* - During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://kit.svelte.dev/docs/hooks#server-hooks-handle)
* - During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
*
* > Cookies will only be passed through if the target host is the same as the SvelteKit application or a more specific subdomain of it.
* You can learn more about making credentialed requests with cookies [here](https://kit.svelte.dev/docs/load#cookies)
*/
fetch: typeof fetch;
/**
Expand Down Expand Up @@ -949,11 +949,13 @@ export interface RequestEvent<
/**
* `fetch` is equivalent to the [native `fetch` web API](https://developer.mozilla.org/en-US/docs/Web/API/fetch), with a few additional features:
*
* - it can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request
* - it can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context)
* - internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call
* - It can be used to make credentialed requests on the server, as it inherits the `cookie` and `authorization` headers for the page request.
* - It can make relative requests on the server (ordinarily, `fetch` requires a URL with an origin when used in a server context).
* - Internal requests (e.g. for `+server.js` routes) go directly to the handler function when running on the server, without the overhead of an HTTP call.
* - During server-side rendering, the response will be captured and inlined into the rendered HTML by hooking into the `text` and `json` methods of the `Response` object. Note that headers will _not_ be serialized, unless explicitly included via [`filterSerializedResponseHeaders`](https://kit.svelte.dev/docs/hooks#server-hooks-handle)
* - During hydration, the response will be read from the HTML, guaranteeing consistency and preventing an additional network request.
*
* > Cookies will only be passed through if the target host is the same as the SvelteKit application or a more specific subdomain of it.
* You can learn more about making credentialed requests with cookies [here](https://kit.svelte.dev/docs/load#cookies)
*/
fetch: typeof fetch;
/**
Expand Down
4 changes: 3 additions & 1 deletion packages/kit/src/runtime/server/fetch.js
Expand Up @@ -51,14 +51,16 @@ export function create_fetch({ event, options, manifest, state, get_cookie_heade
}

if (url.origin !== event.url.origin) {
// allow cookie passthrough for "same-origin"
// Allow cookie passthrough for "credentials: same-origin" and "credentials: include"
// if SvelteKit is serving my.domain.com:
// - domain.com WILL NOT receive cookies
// - my.domain.com WILL receive cookies
// - api.domain.dom WILL NOT receive cookies
// - sub.my.domain.com WILL receive cookies
// ports do not affect the resolution
// leading dot prevents mydomain.com matching domain.com
// Do not forward other cookies for "credentials: include" because we don't know
// which cookie belongs to which domain (browser does not pass this info)
if (`.${url.hostname}`.endsWith(`.${event.url.hostname}`) && credentials !== 'omit') {
const cookie = get_cookie_header(url, request.headers.get('cookie'));
if (cookie) request.headers.set('cookie', cookie);
Expand Down

0 comments on commit 6f36aef

Please sign in to comment.