Last active
January 24, 2023 23:54
-
-
Save arisris/bf1029ff2df0ec952f6519ed280b5e6b to your computer and use it in GitHub Desktop.
Safe call trpc procedure at serverside
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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