Skip to content

Instantly share code, notes, and snippets.

@JPBM135
Last active August 26, 2024 11:47
Show Gist options
  • Save JPBM135/b229bb9d15a3f1482af94da3e342b6eb to your computer and use it in GitHub Desktop.
Save JPBM135/b229bb9d15a3f1482af94da3e342b6eb to your computer and use it in GitHub Desktop.
PrismaGenericbaseService.ts
import { IPaginatedType } from "@common/generics/paginated.type";
import { useConnection } from "@common/utils/connection";
import { paginated } from "@common/utils/paginated";
import { PrismaService } from "@infra/prisma/pprisma.service";
import { Prisma } from "@prisma/client";
import { Result } from "@prisma/client/runtime/library";
type PrismaModelsUnion = Prisma.TypeMap["meta"]["modelProps"];
// Get the model type from the prisma client based on the model name
type PrismaModel<M extends PrismaModelsUnion> = Prisma.TypeMap["model"][Capitalize<M>];
// Get all in common operations for all models
type OperationType = keyof PrismaModel<PrismaModelsUnion>["operations"];
// Get the arguments for each operation
type OperationArguments<M extends PrismaModelsUnion, O extends OperationType> = PrismaModel<M>["operations"][O]["args"];
// The `Result` requires this specific structure to work, thanks 3d triangle
type InternalPrismaReturnMethod<M extends PrismaModelsUnion> = {
[K: symbol]: {
types: {
payload: PrismaModel<M>["payload"];
};
};
};
// The return type for each operation, needs to change based on the arguments provided
type OperationReturnType<M extends PrismaModelsUnion, O extends OperationType, A> = Result<InternalPrismaReturnMethod<M>, A, O>;
export class BaseService<T extends PrismaModelsUnion> {
protected readonly prismaService: PrismaService;
private readonly tableName: T;
constructor(prismaService: PrismaService, tableName: T) {
this.prismaService = prismaService;
this.tableName = tableName;
}
async findMany<A extends OperationArguments<T, "findMany">>(args: A): Promise<IPaginatedType<OperationReturnType<T, "findMany", A>>> {
const [totalCount, edges] = await Promise.all([
this.callPrismaOperation("count", {
where: args.where,
} as OperationArguments<T, "count">),
this.callPrismaOperation("findMany", args as OperationArguments<T, "findMany">),
]);
const connections = useConnection(edges as (PrismaModel<T> & { created_at: Date })[]);
return paginated(connections, totalCount as number);
}
async findFirst<A extends OperationArguments<T, "findFirst">>(args: A): Promise<OperationReturnType<T, "findFirst", A>> {
return this.callPrismaOperation("findFirst", args);
}
async create<A extends OperationArguments<T, "create">>(args: A): Promise<OperationReturnType<T, "create", A>> {
return this.callPrismaOperation("create", args);
}
async update<A extends OperationArguments<T, "update">>(args: A): Promise<OperationReturnType<T, "update", A>> {
return this.callPrismaOperation("update", args);
}
async delete<A extends OperationArguments<T, "delete">>(args: A): Promise<OperationReturnType<T, "delete", A>> {
return this.callPrismaOperation("delete", args);
}
/**
* Ultra hacky way to call prisma operations dynamically and still have type safety.
*/
private callPrismaOperation<O extends OperationType, A extends OperationArguments<T, O>>(
operation: O,
args: A,
): Promise<OperationReturnType<T, O, A>> {
return (
this.prismaService[this.tableName] as {
[K in O]: (...args: any) => any;
}
)[operation](args);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment