Skip to content

Commit 35b696d

Browse files
docs: update canary error handling (#3109)
1 parent a703e79 commit 35b696d

File tree

2 files changed

+87
-60
lines changed

2 files changed

+87
-60
lines changed

docs/canary/advanced/error-handling.md

Lines changed: 80 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -3,80 +3,100 @@ description: |
33
Error pages can be used to customize the page that is shown when an error occurs in the application.
44
---
55

6-
Fresh supports customizing the `404 Not Found`, and the
7-
`500 Internal Server Error` pages. These are shown when a request is made but no
8-
matching route exists, and when a middleware, route handler, or page component
9-
throws an error respectively.
6+
Error pages are used to ensure that your app keeps working and display relevant
7+
feedback to the one who made the request.
108

11-
### 404: Not Found
9+
Fresh supports two kind of error pages:
1210

13-
The 404 page can be customized by creating a `_404.tsx` file in the `routes/`
14-
folder. The file must have a default export that is a regular Preact component.
15-
A props object of type `PageProps` is passed in as an argument.
11+
1. Generic error pages
12+
2. 404 Not found error pages
1613

17-
```tsx routes/_404.tsx
18-
import { PageProps } from "$fresh/server.ts";
14+
> [tip]: Be sure to return the appropriate
15+
> [HTTP Status](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status)
16+
> code. This makes it a lot easier for clients of your app to act appropriately.
17+
> It also makes it easier to find failed requests when going through traces.
1918
20-
export default function NotFoundPage({ url }: PageProps) {
21-
return <p>404 not found: {url.pathname}</p>;
22-
}
19+
## Generic error pages
20+
21+
To add an error page use [`app.onError()`](/docs/canary/concepts/app#onerror).
22+
23+
```ts
24+
const app = new App()
25+
.onError("*", (ctx) => {
26+
console.log(`Error: ${ctx.error}`);
27+
return new Response("Oops!", { status: 500 });
28+
})
29+
.get("/thrower", () => {
30+
throw new Error("fail");
31+
});
2332
```
2433

25-
#### Manually render 404 pages
26-
27-
The `_404.tsx` file will be invoked automatically when no route matches the URL.
28-
In some cases, one needs to manually trigger the rendering of the 404 page, for
29-
example when the route did match, but the requested resource does not exist.
30-
This can be achieved with `ctx.renderNotFound`.
31-
32-
```tsx routes/blog/[slug].tsx
33-
import { Handlers, PageProps } from "$fresh/server.ts";
34-
35-
export const handler: Handlers = {
36-
async GET(req, ctx) {
37-
const blogpost = await fetchBlogpost(ctx.params.slug);
38-
if (!blogpost) {
39-
return ctx.renderNotFound({
40-
custom: "prop",
41-
});
42-
}
43-
return ctx.render({ blogpost });
44-
},
45-
};
46-
47-
export default function BlogpostPage({ data }) {
48-
return (
49-
<article>
50-
<h1>{data.blogpost.title}</h1>
51-
{/* rest of your page */}
52-
</article>
53-
);
54-
}
34+
When you access `/thrower` the error will be caught and the `onError` callback
35+
will be invoked.
36+
37+
You can also nest error pages:
38+
39+
```ts
40+
const app = new App()
41+
// Top level error page
42+
.onError("*", (ctx) => {
43+
return new Response("Oops!", { status: 500 });
44+
})
45+
.onError("/foo/bar", (ctx) => {
46+
return new Response("nested error!", { status: 500 });
47+
})
48+
.get("/foo/bar/thrower", () => {
49+
throw new Error("fail");
50+
});
5551
```
5652

57-
This can also be achieved by throwing an error, if you're uninterested in
58-
passing specific data to your 404 page:
53+
## Not found error
5954

60-
```tsx
61-
import { Handlers } from "$fresh/server.ts";
55+
Not found errors are often treated differently than generic errors. You can both
56+
treat them with the `.onError()` way, but by adding a specific `.notFound()`
57+
handler, Fresh ensures that every 404 error will invoke this callback.
6258

63-
export const handler: Handlers = {
64-
GET(_req, _ctx) {
65-
throw new Deno.errors.NotFound();
66-
},
67-
};
59+
```ts
60+
const app = new App()
61+
// Top level error page
62+
.notFound((ctx) => {
63+
return new Response("Page not found", { status: 404 });
64+
})
65+
.get("/", () => new Response("foo"));
6866
```
6967

70-
### 500: Internal Server Error
68+
Accessing an unknown route like `/invalid` will trigger the `notFound`
69+
middleware. Contrary to generic error pages this handler cannot be nested.
70+
71+
## Throwing HTTP errors
72+
73+
If you need to bail out of execution and need to respond with a particular HTTP
74+
error code, you can use Fresh's `HttpError` class.
7175

72-
The 500 page can be customized by creating a `_500.tsx` file in the `routes/`
73-
folder. The file must have a default export that is a regular Preact component.
74-
A props object of type `PageProps` is passed in as an argument.
76+
```ts
77+
import { HttpError } from "fresh";
7578

76-
```tsx routes/_500.tsx
77-
import { PageProps } from "$fresh/server.ts";
79+
async function authMiddleware(ctx) {
80+
const user = ctx.state.user;
7881

79-
export default function Error500Page({ error }: PageProps) {
80-
return <p>500 internal error: {(error as Error).message}</p>;
82+
// Check if user is authenticated, throw 404 error if not
83+
if (!isAuthenticated(user)) {
84+
throw new HttpError(404);
85+
}
86+
87+
return await ctx.next();
8188
}
8289
```
90+
91+
You can check the status code of the thrown `HttpError` in your error handler:
92+
93+
```ts
94+
app.onError((ctx) => {
95+
if (ctx.error instanceof HttpError) {
96+
const status = ctx.error.status;
97+
return new Response("oops", { status });
98+
}
99+
100+
// ...
101+
});
102+
```

www/static/markdown.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,13 @@ html[data-theme="dark"] {
669669
.markdown-body .admonition.tip .admonition-header {
670670
color: rgb(0, 148, 0);
671671
}
672+
html[data-theme="dark"] .markdown-body .admonition.tip {
673+
background-color: #1f331f;
674+
border-color: var(--color-accent-fg);
675+
}
676+
html[data-theme="dark"] .markdown-body .admonition.tip .admonition-header {
677+
color: var(--color-accent-fg);
678+
}
672679
.markdown-body .admonition.tip {
673680
background-color: rgb(230, 246, 230);
674681
border-color: rgb(0, 148, 0);

0 commit comments

Comments
 (0)