Skip to content

Instantly share code, notes, and snippets.

@ThimoDEV
Last active June 1, 2025 11:57
Show Gist options
  • Save ThimoDEV/27eb1dba1fb447e43175f08093e5e99d to your computer and use it in GitHub Desktop.
Save ThimoDEV/27eb1dba1fb447e43175f08093e5e99d to your computer and use it in GitHub Desktop.
Make a file named: defineNitroTRPCEventHandler.ts
import {
type AnyRouter,
type ProcedureType,
type TRPCError,
type inferRouterContext,
type inferRouterError,
} from "@trpc/server";
import { type ResponseMeta } from "@trpc/server/http";
import { fetchRequestHandler } from "@trpc/server/adapters/fetch";
import { TRPCResponse } from "@trpc/server/rpc";
import {
type H3Event,
defineEventHandler,
isMethod,
readBody
} from "h3";
import { MaybePromise } from "/types/promise";
import { getPath } from "/utils/internals";
export type CreateContextFn<
TRouter extends AnyRouter,
= (
event: H3Event
) => MaybePromise<inferRouterContext>;
export interface ResponseMetaFnPayload<
TRouter extends AnyRouter,
{
data: TRPCResponse<
unknown,
inferRouterError
[];
ctx?: inferRouterContext;
paths?: readonly string[];
type: ProcedureType | "unknown";
errors: TRPCError[];
}
export type ResponseMetaFn<
TRouter extends AnyRouter,
= (
opts: ResponseMetaFnPayload
) => ResponseMeta;
export interface OnErrorPayload<
TRouter extends AnyRouter,
{
error: TRPCError;
type: ProcedureType | "unknown";
path: string | undefined;
req: Request;
input: unknown;
ctx: undefined | inferRouterContext;
}
export type OnErrorFn =
(opts: OnErrorPayload) => void;
export const defineNitroTRPCEventHandler = <
TRouter extends AnyRouter,
({
router,
createContext,
responseMeta,
onError,
}: {
router: TRouter;
createContext?: (
e: H3Event
) =>
| inferRouterContext
| Promise<inferRouterContext>;
responseMeta?: ResponseMetaFn;
onError?: OnErrorFn;
}) => {
return defineEventHandler(async (event) => {
const req = new Request(
getRequestURL(event),
{
method: event.method,
headers: event.headers,
body: isMethod(event, "GET")
? undefined
: await readBody(event),
}
);
return fetchRequestHandler({
req,
router,
endpoint: getPath(event),
createContext: createContext
? () => createContext(event)
: undefined,
responseMeta,
onError,
});
});
};
Place below code in a file internals.ts in the utils folder in nitro
import { TRPCError } from "@trpc/server";
import { type H3Event } from "h3";
export const getPath = (
event: H3Event
): string => {
const params = event.context.params;
if (!params) {
// Throw an error if the trpc parameter is not a string or an array:
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message:
"Please ensure that the trpc parameter is defined in your routes file e.g., ./routes/[trpc].ts",
cause: "Nitro Routing Configuration",
});
}
if (typeof params.trpc === "string") {
return params.trpc;
}
if (typeof params.trpc === "string") {
return params.trpc;
}
// Throw an error if the trpc parameter is not a string or an array:
throw new TRPCError({
code: "INTERNAL_SERVER_ERROR",
message:
"Please ensure that the trpc parameter is defined in your routes file e.g., ./routes/[trpc].ts",
cause: "Nitro Routing Configuration",
});
};
Make a folder and file in: trpc/appRouter.ts
import {
protectedProcedure,
publicProcedure,
router,
} from "~~/src/lib/trpc";
export const appRouter = router({
healthCheck: publicProcedure.query(() => "OK"),
privateData: protectedProcedure.query(
({ ctx }) => ({
message: "This is private",
user: ctx.session.user,
})
),
});
export type AppRouter = typeof appRouter;
Add a folder/file in: lib/trpc.ts
import {
initTRPC,
TRPCError,
} from "@trpc/server";
import type { Context } from "./context";
import superjson from "superjson";
const t = initTRPC.context().create({
transformer: superjson,
});
export const router = t.router;
export const publicProcedure = t.procedure;
export const protectedProcedure = t.procedure.use(
({ ctx, next }) => {
if (!ctx.session) {
throw new TRPCError({
code: "UNAUTHORIZED",
message: "Authentication required",
});
}
return next({
ctx: { ...ctx, session: ctx.session },
});
}
);
Create a file in: lib/context.ts
import type { H3Event } from "h3";
import { auth } from "./auth";
export async function createContext(
event: H3Event
) {
const session = await auth.api.getSession({
headers: event.headers,
});
return { session, event };
}
export type Context = Awaited<ReturnType;
export type MaybePromise = T | Promise;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment