diff --git a/documentation/docs/20-core-concepts/20-load.md b/documentation/docs/20-core-concepts/20-load.md index f37161747669..4bd6b3f3cd90 100644 --- a/documentation/docs/20-core-concepts/20-load.md +++ b/documentation/docs/20-core-concepts/20-load.md @@ -374,7 +374,7 @@ export async function load({ params, parent }) { ## Errors -If an error is thrown during `load`, the nearest [`+error.svelte`](routing#error) will be rendered. For _expected_ errors, use the `error` helper from `@sveltejs/kit` to specify the HTTP status code and an optional message: +If an error is thrown during `load`, the nearest [`+error.svelte`](routing#error) will be rendered. For [_expected_](/docs/errors#expected-errors) errors, use the `error` helper from `@sveltejs/kit` to specify the HTTP status code and an optional message: ```js /// file: src/routes/admin/+layout.server.js @@ -404,11 +404,15 @@ export function load({ locals }) { } ``` -If an _unexpected_ error is thrown, SvelteKit will invoke [`handleError`](hooks#shared-hooks-handleerror) and treat it as a 500 Internal Error. +Calling `error(...)` will throw an exception, making it easy to stop execution from inside helper functions. + +If an [_unexpected_](/docs/errors#unexpected-errors) error is thrown, SvelteKit will invoke [`handleError`](hooks#shared-hooks-handleerror) and treat it as a 500 Internal Error. + +> [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the error yourself ## Redirects -To redirect users, use the `redirect` helper from `@sveltejs/kit` to specify the location to which they should be redirected alongside a `3xx` status code. +To redirect users, use the `redirect` helper from `@sveltejs/kit` to specify the location to which they should be redirected alongside a `3xx` status code. Like `error(...)`, calling `redirect(...)` will throw an exception, making it easy to stop execution from inside helper functions. ```js /// file: src/routes/user/+layout.server.js @@ -437,24 +441,31 @@ export function load({ locals }) { In the browser, you can also navigate programmatically outside of a `load` function using [`goto`](modules#$app-navigation-goto) from [`$app.navigation`](modules#$app-navigation). +> [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the `redirect` yourself + ## Streaming with promises -Promises at the _top level_ of the returned object will be awaited, making it easy to return multiple promises without creating a waterfall. When using a server `load`, _nested_ promises will be streamed to the browser as they resolve. This is useful if you have slow, non-essential data, since you can start rendering the page before all the data is available: +When using a server `load`, promises will be streamed to the browser as they resolve. This is useful if you have slow, non-essential data, since you can start rendering the page before all the data is available: ```js -/// file: src/routes/+page.server.js +/// file: src/routes/blog/[slug]/+page.server.js +// @filename: ambient.d.ts +declare global { + const loadPost: (slug: string) => Promise<{ title: string, content: string }>; + const loadComments: (slug: string) => Promise<{ content: string }>; +} + +export {}; + +// @filename: index.js +// ---cut--- /** @type {import('./$types').PageServerLoad} */ -export function load() { +export async function load({ params }) { return { - one: Promise.resolve(1), - two: Promise.resolve(2), - streamed: { - three: new Promise((fulfil) => { - setTimeout(() => { - fulfil(3) - }, 1000); - }) - } + // make sure the `await` happens at the end, otherwise we + // can't start loading comments until we've loaded the post + comments: loadComments(params.slug), + post: await loadPost(params.slug) }; } ``` @@ -462,28 +473,24 @@ export function load() { This is useful for creating skeleton loading states, for example: ```svelte - + -
- one: {data.one} -
-- two: {data.two} -
-- three: - {#await data.streamed.three} - Loading... - {:then value} - {value} - {:catch error} - {error.message} - {/await} -
+{comment.content}
+ {/each} +{:catch error} +error loading comments: {error.message}
+{/await} ``` When streaming data, be careful to handle promise rejections correctly. More specifically, the server could crash with an "unhandled promise rejection" error if a lazy-loaded promise fails before rendering starts (at which point it's caught) and isn't handling the error in some way. When using SvelteKit's `fetch` directly in the `load` function, SvelteKit will handle this case for you. For other promises, it is enough to attach a noop-`catch` to the promise to mark it as handled. @@ -496,21 +503,21 @@ export function load({ fetch }) { ok_manual.catch(() => {}); return { - streamed: { - ok_manual, - ok_fetch: fetch('/fetch/that/could/fail'), - dangerous_unhandled: Promise.reject() - } + ok_manual, + ok_fetch: fetch('/fetch/that/could/fail'), + dangerous_unhandled: Promise.reject() }; } ``` > On platforms that do not support streaming, such as AWS Lambda, responses will be buffered. This means the page will only render once all promises resolve. If you are using a proxy (e.g. NGINX), make sure it does not buffer responses from the proxied server. -> Streaming data will only work when JavaScript is enabled. You should avoid returning nested promises from a universal `load` function if the page is server rendered, as these are _not_ streamed — instead, the promise is recreated when the function reruns in the browser. +> Streaming data will only work when JavaScript is enabled. You should avoid returning promises from a universal `load` function if the page is server rendered, as these are _not_ streamed — instead, the promise is recreated when the function reruns in the browser. > The headers and status code of a response cannot be changed once the response has started streaming, therefore you cannot `setHeaders` or throw redirects inside a streamed promise. +> [In SvelteKit 1.x](migrating-to-sveltekit-2#top-level-promises-are-no-longer-awaited) top-level promises were automatically awaited, only nested promises were streamed. + ## Parallel loading When rendering (or navigating to) a page, SvelteKit runs all `load` functions concurrently, avoiding a waterfall of requests. During client-side navigation, the result of calling multiple server `load` functions are grouped into a single response. Once all `load` functions have returned, the page is rendered. diff --git a/documentation/docs/30-advanced/25-errors.md b/documentation/docs/30-advanced/25-errors.md index 81f216cd2ff3..7ca1a0580889 100644 --- a/documentation/docs/30-advanced/25-errors.md +++ b/documentation/docs/30-advanced/25-errors.md @@ -40,7 +40,7 @@ export async function load({ params }) { } ``` -This tells SvelteKit to set the response status code to 404 and render an [`+error.svelte`](routing#error) component, where `$page.error` is the object provided as the second argument to `error(...)`. +This throws an exception that SvelteKit catches, causing it to set the response status code to 404 and render an [`+error.svelte`](routing#error) component, where `$page.error` is the object provided as the second argument to `error(...)`. ```svelte @@ -67,6 +67,8 @@ error(404, { +error(404, 'Not found'); ``` +> [In SvelteKit 1.x](migrating-to-sveltekit-2#redirect-and-error-are-no-longer-thrown-by-you) you had to `throw` the `error` yourself + ## Unexpected errors An _unexpected_ error is any other exception that occurs while handling a request. Since these can contain sensitive information, unexpected error messages and stack traces are not exposed to users.