Skip to content

Instantly share code, notes, and snippets.

@arisris
Last active January 24, 2023 23:54
Show Gist options
  • Save arisris/bf1029ff2df0ec952f6519ed280b5e6b to your computer and use it in GitHub Desktop.
Save arisris/bf1029ff2df0ec952f6519ed280b5e6b to your computer and use it in GitHub Desktop.
Safe call trpc procedure at serverside
import {
AnyProcedure,
AnyRouter,
inferRouterContext,
ProcedureArgs,
ProcedureType,
procedureTypes,
TRPCError,
} from "@trpc/server";
import { createRecursiveProxy } from "@trpc/server/shared";
export type TRPCModelOptions<TRouter extends AnyRouter> = {
router: TRouter;
context: inferRouterContext<TRouter>;
};
type TRPCModelResultSuccess<TData> = { success: true; data: TData };
type TRPCModelResultError<TError> = { success: false; error: TError };
type TRPCModelResult<TData, TError> =
| TRPCModelResultSuccess<TData>
| TRPCModelResultError<TError>;
type DecorateProcedure<TProcedure extends AnyProcedure> = (
input: ProcedureArgs<TProcedure["_def"]>[0],
) => Promise<
TRPCModelResult<TProcedure["_def"]["_output_out"], TRPCError | Error>
>;
type TRPCModel<TProcedure extends AnyProcedure> = {
[TKey in keyof TProcedure]: TProcedure[TKey] extends AnyRouter
? TRPCModel<TProcedure[TKey]["_def"]["record"]>
: TProcedure[TKey] extends AnyProcedure
? DecorateProcedure<TProcedure[TKey]>
: any;
};
export function createTRPCModel<TRouter extends AnyRouter>(
{ router, context }: TRPCModelOptions<TRouter>,
) {
const proxy = createRecursiveProxy(
async ({ path, args }) => {
if (path.length === 1 && procedureTypes.includes(path[0] as never)) {
return {
success: false,
error: new TRPCError({
code: "METHOD_NOT_SUPPORTED",
message: "Unsupported",
}),
};
}
const fullPath = path.join(".");
const procedure = router["_def"].procedures[fullPath] as AnyProcedure;
let type: ProcedureType = "query";
if (procedure._def.mutation) {
type = "mutation";
} else if (procedure._def.subscription) {
type = "subscription";
}
try {
const data = await procedure({
ctx: context,
path: fullPath,
rawInput: args[0],
type,
});
return { success: true, data };
} catch (error) {
return {
success: false,
error,
};
}
},
);
return proxy as TRPCModel<TRouter["_def"]["procedures"]>;
}
// test
const context = {};
const caller = createTRPCModel(router, context);
const {data, error} = await caller.yourProcedure(...args);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment