
On This Page
I have been using tRPC for quite a while now and I'm absoluting loing it. While using though, I realized that I kept repeating imports in my SvelteKit +page.server
files.
LIke any good developer, I decided to fix this problem. I created a utility function that would allow me to import tRPC only once and then use it in all my +page.server
files.
Another crucial thing this utility tackles is rethrowing the right SvelteKit error object. The original error object thrown by tRPC is not the right one for SvelteKit and results in an unexpected error.
The utility function
All you need to do is import your appRouter
and createTRPCContext
functions and pass them to the utility function. Keep in mind that these might be named differently in your project.
The utility function will return a caller
object that you can use to call your tRPC methods.
import { appRouter } from '$lib/api/root';
import { createTRPCContext } from '$lib/api/trpc';
import { type RequestEvent, error } from '@sveltejs/kit';
import { TRPCError } from '@trpc/server';
import { getHTTPStatusCodeFromError } from '@trpc/server/http';
export async function trpcLoad<
Event extends RequestEvent<Partial<Record<string, string>>, string | null>,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Method extends (caller: ReturnType<typeof appRouter.createCaller>) => Promise<any>
>(event: Event, method: Method): Promise<ReturnType<Method>> {
try {
const caller = appRouter.createCaller(await createTRPCContext(event));
return await method(caller);
} catch (e) {
if (e instanceof TRPCError) {
const httpCode = getHTTPStatusCodeFromError(e);
throw error(httpCode, e.message);
}
throw error(500, 'Unknown error');
}
}
To illustrate how this utility can be used, here is a sample usage in +page.server
. This eliminates repeating imports while allowing the ability to load in parallel.
Before
import { appRouter } from '$lib/api/root';
import { createTRPCContext } from '$lib/api/trpc';
import type { PageServerLoad } from './$types';
export const load = (async (e) => {
return {
user: appRouter.createCaller(await createTRPCContext(event)).users.get(e.params.username),
likes: appRouter.createCaller(await createTRPCContext(event)).likes.get(e.params.username)
};
}) satisfies PageServerLoad;
After
import { trpcLoad } from '$lib/api/trpc-load';
import type { PageServerLoad } from './$types';
export const load = (async (e) => {
return {
user: trpcLoad(e, (t) => t.users.get(e.params.username)),
likes: trpcLoad(e, (t) => t.likes.get(e.params.username))
};
}) satisfies PageServerLoad;
Epic right? This looks so much cleaner and is much easier to maintain.
In conclusion, tRPC combined with SvelteKit offers a powerful solution to manage server-side procedures.
Hopefully this utility will help you optimize your code imports and avoid unexpected errors.