Error Handing
Built-In Errors
Blitz comes with a number of useful errors you can use throughout your application.
AuthenticationError
name
: "AuthenticationError"statusCode
: 401- Default
message
: "You must be logged in to access this"
CSRFTokenMismatchError
name
: "CSRFTokenMismatchError"statusCode
: 401- Default
message
: "You must be logged in to access this"
AuthorizationError
name
: "AuthorizationError"statusCode
: 403- Default
message
: "You are not authorized to access this"
NotFoundError
name
: "NotFoundError"statusCode
: 404- Default
message
: "This could not be found"
To use, import from
blitz
and use like any Javascript Error. If you're curious, you can see the source code for these.import {AuthenticationError} from "blitz"try {throw new AuthenticationError()} catch (error) {if (error.name === "AuthenticationError") {// Handle this error appropriately, like show a login screenconsole.log(error.statusCode)console.log(error.message)}}
You can throw these or any other errors from anywhere in your app, whether on the server or on the client.
Catching and Handling Errors on the Client
By default, new Blitz applications come with
react-error-boundary
installed with a top-level ErrorBoundary
and FallbackComponent
in app/pages/_app.tsx
.It looks something like this:
// app/pages/_app.tsximport {AppProps, ErrorComponent} from "blitz"import {ErrorBoundary} from "react-error-boundary"import {queryCache} from "react-query"import LoginForm from "app/auth/components/LoginForm"export default function App({Component, pageProps}: AppProps) {return (<ErrorBoundaryFallbackComponent={RootErrorFallback}onReset={() => {// This ensures the Blitz useQuery hooks will automatically refetch// data any time you reset the error boundaryqueryCache.resetErrorBoundaries()}}><Component {...pageProps} /></ErrorBoundary>)}function RootErrorFallback({error, resetErrorBoundary}) {if (error.name === "AuthenticationError") {return <LoginForm onSuccess={resetErrorBoundary} />} else if (error.name === "AuthorizationError") {return (<ErrorComponentstatusCode={error.statusCode}title="Sorry, you are not authorized to access this"/>)} else {return (<ErrorComponent statusCode={error.statusCode || 400} title={error.message || error.name} />)}}
That means all errors will at least be caught at the root level. However, you can also add
<ErrorBoundary>
anywhere else in your app for more localized error handiling. If an error is caught by an <ErrorBoundary>
somewhere down inside your app tree, then it will not reach the root ErrorBoundary unless you re-throw it.Handing Server Errors on the Client
A really awesome feature of Blitz is that you can throw any error from a Blitz query or mutation and then use an ErrorBoundary on the frontend to catch and handle it.
For example, with the above
_app.tsx
, you can throw AuthenticationError
inside a Blitz query and then a login screen will automatically show in the client because that root ErrorBoundary is rendering <LoginForm>
if error.name === 'AuthenticationError'
.Custom Errors
For errors other than what Blitz provides, it's recommended to create custom Error classes. You can then add custom data attributes that help you handle the error.
Here's an example of how to create a custom error. It's a Javascript class, so you can be as creative as you want.
export class UsernameTakenError extends Error {name = "UsernameTakenError"constructor({suggestedUserName}) {super()this.suggestedUserName = suggestedUserName}}throw new UsernameTakenError({suggestedUserName: "second_best"})
Then on the client, you can use a
FallbackComponent
like above, or you can handle the error in your form submit handler like this.<FormonSubmit={async (values) => {try {await setUsername(values.username)} catch (error) {if (error.name === "UsernameTakenError") {setSuggestedUsername(error.suggestedUserName)}}}}/>
Error Pages
See the
Error Pages documentation to learn about custom 404 and 500 error pages.