Skip to content

Commit

Permalink
feat: flexible 'createRouter' typings (#654)
Browse files Browse the repository at this point in the history
Co-authored-by: Yiming <yiming@whimslab.io>
  • Loading branch information
mateus-p and ymc9 authored Aug 30, 2023
1 parent 76c12f5 commit e147412
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 11 deletions.
75 changes: 66 additions & 9 deletions packages/plugins/trpc/src/generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,35 +89,85 @@ function createAppRouter(
const prismaImport = getPrismaClientImportSpec(zmodel, path.dirname(indexFile));
appRouter.addImportDeclarations([
{
namedImports: ['AnyRootConfig'],
namedImports: ['type AnyRootConfig', 'type Procedure', 'type ProcedureParams', 'type ProcedureType'],
moduleSpecifier: '@trpc/server',
},
{
namedImports: ['PrismaClient'],
namedImports: ['type PrismaClient', 'type Prisma'],
moduleSpecifier: prismaImport,
isTypeOnly: true,
},
{
namedImports: ['createRouterFactory', 'AnyRouter'],
namedImports: ['type createRouterFactory', 'AnyRouter'],
moduleSpecifier: '@trpc/server/dist/core/router',
},
{
namedImports: ['createBuilder'],
namedImports: ['type ProcedureBuilder'],
moduleSpecifier: '@trpc/server/dist/core/internals/procedureBuilder',
},
{ defaultImport: 'z', moduleSpecifier: 'zod', isTypeOnly: true },
]);

appRouter.addStatements(`
${/** to be used by the other routers without making a bigger commit */ ''}
export { PrismaClient } from '${prismaImport}';
export type BaseConfig = AnyRootConfig;
export type RouterFactory<Config extends BaseConfig> = ReturnType<
typeof createRouterFactory<Config>
>;
export type ProcBuilder<Config extends BaseConfig> = ReturnType<
typeof createBuilder<Config>
>;
${
/** this is needed in order to prevent type errors between a procedure and a middleware-extended procedure */ ''
}
export type ProcBuilder<Config extends BaseConfig> = ProcedureBuilder<{
_config: Config;
_ctx_out: Config['$types']['ctx'];
_input_in: any;
_input_out: any;
_output_in: any;
_output_out: any;
_meta: Config['$types']['meta'];
}>;
type ExtractParamsFromProcBuilder<Builder extends ProcedureBuilder<any>> =
Builder extends ProcedureBuilder<infer P> ? P : never;
type FromPromise<P extends Promise<any>> = P extends Promise<infer T>
? T
: never;
${/** workaround to avoid creating 'typeof unsetMarker & object' on the procedure output */ ''}
type Join<A, B> = A extends symbol ? B : A & B;
${
/** you can name it whatever you want, but this is to make sure that
the types from the middleware and the procedure are correctly merged */ ''
}
export type ProcReturns<
PType extends ProcedureType,
PBuilder extends ProcBuilder<BaseConfig>,
ZType extends z.ZodType,
PPromise extends Prisma.PrismaPromise<any>
> = Procedure<
PType,
ProcedureParams<
ExtractParamsFromProcBuilder<PBuilder>["_config"],
ExtractParamsFromProcBuilder<PBuilder>["_ctx_out"],
Join<ExtractParamsFromProcBuilder<PBuilder>["_input_in"], z.infer<ZType>>,
Join<ExtractParamsFromProcBuilder<PBuilder>["_input_out"], z.infer<ZType>>,
Join<
ExtractParamsFromProcBuilder<PBuilder>["_output_in"],
FromPromise<PPromise>
>,
Join<
ExtractParamsFromProcBuilder<PBuilder>["_output_out"],
FromPromise<PPromise>
>,
ExtractParamsFromProcBuilder<PBuilder>["_meta"]
>
>;
export function db(ctx: any) {
if (!ctx.prisma) {
throw new Error('Missing "prisma" field in trpc context');
Expand Down Expand Up @@ -233,7 +283,14 @@ function generateModelCreateRouter(

modelRouter.addImportDeclarations([
{
namedImports: ['type RouterFactory', 'type ProcBuilder', 'type BaseConfig', 'db'],
namedImports: [
'type RouterFactory',
'type ProcBuilder',
'type BaseConfig',
'type ProcReturns',
'type PrismaClient',
'db',
],
moduleSpecifier: '.',
},
]);
Expand Down
14 changes: 12 additions & 2 deletions packages/plugins/trpc/src/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,24 @@ export function generateProcedure(
writer.write(`
${opType}: procedure.input(${typeName}).query(({ctx, input}) => checkRead(db(ctx).${lowerCaseFirst(
modelName
)}.${prismaMethod}(input as any))),
)}.${prismaMethod}(input as any))) as ProcReturns<
"query",
Proc,
(typeof ${upperCaseFirst(modelName)}InputSchema)["${opType.replace('OrThrow', '')}"],
ReturnType<PrismaClient["${lowerCaseFirst(modelName)}"]["${opType}"]>
>,
`);
} else if (procType === 'mutation') {
// the cast "as any" is to circumvent a TS compiler misfired error in certain cases
writer.write(`
${opType}: procedure.input(${typeName}).mutation(async ({ctx, input}) => checkMutate(db(ctx).${lowerCaseFirst(
modelName
)}.${prismaMethod}(input as any))),
)}.${prismaMethod}(input as any))) as ProcReturns<
"mutation",
Proc,
(typeof ${upperCaseFirst(modelName)}InputSchema)["${opType.replace('OrThrow', '')}"],
ReturnType<PrismaClient["${lowerCaseFirst(modelName)}"]["${opType}"]>
>,
`);
}
}
Expand Down

0 comments on commit e147412

Please sign in to comment.