Handling errors
Whenever an error occurs in a procedure, tRPC responds to the client with an object that includes
an "error" property. This property contains all the information that you need to handle the error
in the client. Here's an example of how you can handle the error in your SvelteKit page (look at
the try/catch
block in the handleSave
function):
routes/authors/+page.svelte
<script lang="ts">
import { invalidateAll } from '$app/navigation';
import AuthorizationAlert from '$lib/components/AuthorizationAlert.svelte';
import DataTable from '$lib/components/DataTable.svelte';
import ModalEditor from '$lib/components/ModalEditor.svelte';
import TextInput from '$lib/components/inputs/TextInput.svelte';
import TextareaInput from '$lib/components/inputs/TextareaInput.svelte';
import { trpc } from '$lib/trpc/client';
import type { RouterInputs } from '$lib/trpc/router';
import { TRPCClientError } from '@trpc/client';
import type { PageData } from './$types';
export let data: PageData;
let busy = false;
let item: RouterInputs['authors']['save'] | null; // 👈 we're using a helper type
let errors: { message: string; path: string[] }[] | null = null;
let needsAuthorization = false;
const handleAdd = async () => {
if (!data.isAuthenticated) {
needsAuthorization = true;
return;
}
item = { id: null, firstName: '', lastName: '', bio: '' };
};
const handleEdit = async (e: CustomEvent<string>) => {
if (!data.isAuthenticated) {
needsAuthorization = true;
return;
}
busy = true;
item = await trpc().authors.load.query(e.detail);
busy = false;
};
const handleDelete = async (e: CustomEvent<string>) => {
if (!data.isAuthenticated) {
needsAuthorization = true;
return;
}
busy = true;
await trpc().authors.delete.mutate(e.detail);
await invalidateAll();
busy = false;
};
const handleCancel = () => {
item = null;
errors = null;
};
const handleSave = async (e: CustomEvent<RouterInputs['authors']['save']>) => {
if (!data.isAuthenticated) {
needsAuthorization = true;
return;
}
busy = true;
try {
await trpc().authors.save.mutate(e.detail);
item = null;
await invalidateAll();
} catch (err) {
if (err instanceof TRPCClientError) {
errors = JSON.parse(err.message);
} else {
throw err;
}
} finally {
busy = false;
}
};
</script>
<svelte:head>
<title>Authors • Bookstall</title>
</svelte:head>
<DataTable
{busy}
title="Authors"
items={data.authors}
columns={[
{
title: 'Name',
grow: true,
accessor: ({ firstName, lastName }) => `${firstName} ${lastName}`
},
{
title: 'Books',
align: 'right',
accessor: (author) => author._count.books
}
]}
on:add={handleAdd}
on:edit={handleEdit}
on:delete={handleDelete}
/>
<ModalEditor {item} itemName="author" on:cancel={handleCancel} on:save={handleSave}>
<div class="grid">
<TextInput name="firstName" label="First name" required {errors} {item} />
<TextInput name="lastName" label="Last name" required {errors} {item} />
</div>
<TextareaInput name="bio" label="Bio" {errors} {item} />
</ModalEditor>
<AuthorizationAlert visible={needsAuthorization} on:close={() => (needsAuthorization = false)} />
Please refer to the error handling page in the tRPC documentation for more information.
You can also supply an onError
handler to the createTRPCHandle
method in
your hooks.server.ts
, which could be useful, for instance, if you want to log all
errors to a service like Sentry:
hooks.server.ts
import { createContext } from '$lib/trpc/context';
import { router } from '$lib/trpc/router';
import type { Handle } from '@sveltejs/kit';
import { createTRPCHandle } from 'trpc-sveltekit';
export const handle: Handle = createTRPCHandle({
router,
createContext,
onError: ({ type, path, error }) =>
console.error(`Encountered error while trying to process ${type} @ ${path}:`, error)
});