Last active
March 6, 2025 17:54
-
-
Save aniravi24/b1f97ab2d7e6ec54b8309ed0452745a0 to your computer and use it in GitHub Desktop.
This file contains hidden or 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 { Prisma } from "@prisma/client"; | |
import type { Replace, UnionToTuple } from "type-fest"; | |
import { Effect, Option } from "effect"; | |
import { UnknownException } from "effect/Cause"; | |
type PrismaModelOp = Exclude< | |
// You can import operation types from the generated Prisma client | |
Operation, | |
| "$executeRaw" | |
| "$executeRawUnsafe" | |
| "$queryRaw" | |
| "$queryRawUnsafe" | |
| "$runCommandRaw" | |
| "aggregateRaw" | |
| "findFirst" | |
| "findFirstOrThrow" | |
| "findRaw" | |
| "findUnique" | |
| "findUniqueOrThrow" | |
>; | |
const PrismaModelOps: UnionToTuple<PrismaModelOp> = [ | |
"findMany", | |
"create", | |
"createMany", | |
"createManyAndReturn", | |
"update", | |
"updateMany", | |
"updateManyAndReturn", | |
"upsert", | |
"delete", | |
"deleteMany", | |
"aggregate", | |
"count", | |
"groupBy", | |
] as const; | |
export const prismaEffectExtension = { | |
client: { | |
// $executeRaw: (query: TemplateStringsArray | Sql, ...values: any[]) => PrismaPromise<number> | |
$executeRawEffect: function <T>( | |
this: T, | |
// This type will work after generating the Prisma client | |
query: Prisma.Sql | TemplateStringsArray, | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
...values: any[] | |
): Effect.Effect<number, UnknownException, never> { | |
return Effect.tryPromise<number>(() => { | |
const prismaExtensionContextClient = Prisma.getExtensionContext(this); | |
// @ts-expect-error Can't predict these in advance | |
return prismaExtensionContextClient["$executeRaw"](query, ...values); | |
}); | |
}, | |
// $executeRawUnsafe: (query: string, ...values: any[]) => PrismaPromise<number> | |
$executeRawUnsafeEffect: function <T>( | |
this: T, | |
query: string, | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
...values: any[] | |
): Effect.Effect<number, UnknownException, never> { | |
return Effect.tryPromise<number>(() => { | |
const prismaExtensionContextClient = Prisma.getExtensionContext(this); | |
// @ts-expect-error Can't predict these in advance | |
return prismaExtensionContextClient["$executeRawUnsafe"]( | |
query, | |
...values | |
); | |
}); | |
}, | |
// $queryRaw: <T = unknown>(query: TemplateStringsArray | Sql, ...values: any[]) => PrismaPromise<T> | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
$queryRawEffect: function <A = unknown, T = any>( | |
this: T, | |
query: Prisma.Sql | TemplateStringsArray, | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
...values: any[] | |
): Effect.Effect<A, UnknownException, never> { | |
return Effect.tryPromise<A>(() => { | |
const prismaExtensionContextClient = Prisma.getExtensionContext(this); | |
// @ts-expect-error Can't predict these in advance | |
return prismaExtensionContextClient["$queryRaw"](query, ...values); | |
}); | |
}, | |
// $queryRawTyped: <T>(query: TypedSql<unknown[], T>) => PrismaPromise<T[]> | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
$queryRawTypedEffect: function <A = unknown, T = any>( | |
this: T, | |
query: TypedSql<unknown[], T> | |
): Effect.Effect<A, UnknownException, never> { | |
return Effect.tryPromise<A>(() => { | |
const prismaExtensionContextClient = Prisma.getExtensionContext(this); | |
// @ts-expect-error Can't predict these in advance | |
return prismaExtensionContextClient["$queryRawTyped"](query); | |
}); | |
}, | |
// $queryRawUnsafe: <T = unknown>(query: string, ...values: any[]) => PrismaPromise<T> | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
$queryRawUnsafeEffect: function <A = unknown, T = any>( | |
this: T, | |
query: string, | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
...values: any[] | |
): Effect.Effect<A, UnknownException, never> { | |
return Effect.tryPromise<A>(() => { | |
const prismaExtensionContextClient = Prisma.getExtensionContext(this); | |
// @ts-expect-error Can't predict these in advance | |
return prismaExtensionContextClient["$queryRawUnsafe"]( | |
query, | |
...values | |
); | |
}); | |
}, | |
}, | |
model: { | |
$allModels: { | |
...(Object.fromEntries( | |
PrismaModelOps.map((method) => [ | |
`${method}Effect`, | |
function <T, A, O extends typeof method>( | |
// `this` is the current type (for example | |
// it might be `prisma.user` at runtime). | |
this: T, | |
x?: Prisma.Exact< | |
A, | |
// For `customCall`, use the arguments from model `T` and the | |
// operation `findFirst`. Add `customProperty` to the operation. | |
Prisma.Args<T, O> | |
> | |
// Get the correct result types for the model of type `T`, | |
// and the arguments of type `A` for `findFirst`. | |
// `Prisma.Result` computes the result for a given operation | |
// such as `select {id: true}` in function `main` below. | |
//, | |
): Effect.Effect<Prisma.Result<T, A, O>, UnknownException, never> { | |
return Effect.tryPromise<Prisma.Result<T, A, O>>(() => { | |
// eslint-disable-next-line no-invalid-this | |
const prismaExtensionContextClient = | |
Prisma.getExtensionContext(this); | |
// @ts-expect-error Can't predict these in advance | |
// eslint-disable-next-line @typescript-eslint/no-explicit-any | |
return prismaExtensionContextClient[method](x) as any; | |
}); | |
}, | |
]) | |
) as { | |
[K in `${(typeof PrismaModelOps)[number]}Effect`]: < | |
T, | |
A, | |
O extends Replace<K, "Effect", "">, | |
>( | |
this: T, | |
x?: Prisma.Exact<A, Prisma.Args<T, O>> | |
) => Effect.Effect<Prisma.Result<T, A, O>, UnknownException, never>; | |
}), | |
findFirstEffect<T, A, O extends "findFirstOrThrow">( | |
this: T, | |
x?: Prisma.Exact<A, Prisma.Args<T, O>> | |
): Effect.Effect<Prisma.Result<T, A, O>, UnknownException, never> { | |
return Effect.tryPromise<Prisma.Result<T, A, O>>(() => { | |
const prismaExtensionContextClient = Prisma.getExtensionContext(this); | |
// @ts-expect-error Can't predict these in advance | |
return prismaExtensionContextClient.findFirstOrThrow(x); | |
}); | |
}, | |
findFirstOption<T, A, O extends "findFirst">( | |
this: T, | |
x?: Prisma.Exact<A, Prisma.Args<T, O>> | |
): Effect.Effect< | |
Option.Option<NonNullable<Prisma.Result<T, A, O>>>, | |
UnknownException, | |
never | |
> { | |
return Effect.tryPromise<Prisma.Result<T, A, O>>(() => { | |
const prismaExtensionContextClient = Prisma.getExtensionContext(this); | |
// @ts-expect-error Can't predict these in advance | |
return prismaExtensionContextClient.findFirst(x); | |
}).pipe(Effect.map((result) => Option.fromNullable(result))); | |
}, | |
findUniqueEffect<T, A, O extends "findUniqueOrThrow">( | |
this: T, | |
x?: Prisma.Exact<A, Prisma.Args<T, O>> | |
): Effect.Effect<Prisma.Result<T, A, O>, UnknownException, never> { | |
return Effect.tryPromise<Prisma.Result<T, A, O>>(() => { | |
const prismaExtensionContextClient = Prisma.getExtensionContext(this); | |
// @ts-expect-error Can't predict these in advance | |
return prismaExtensionContextClient.findUniqueOrThrow(x); | |
}); | |
}, | |
findUniqueOption<T, A, O extends "findUnique">( | |
this: T, | |
x?: Prisma.Exact<A, Prisma.Args<T, O>> | |
): Effect.Effect< | |
Option.Option<NonNullable<Prisma.Result<T, A, O>>>, | |
UnknownException, | |
never | |
> { | |
return Effect.tryPromise<Prisma.Result<T, A, O>>(() => { | |
const prismaExtensionContextClient = Prisma.getExtensionContext(this); | |
// @ts-expect-error Can't predict these in advance | |
return prismaExtensionContextClient.findUnique(x); | |
}).pipe(Effect.map((result) => Option.fromNullable(result))); | |
}, | |
}, | |
}, | |
}; |
This adds the Prisma methods suffixed with the word Effect
, so you can call prisma.someTable.findManyEffect({ testing: true })
Updated to Effect 3.12
Updated to fix the client operations, need to explicitly list them as they are all different
Nice!
Updated to make the findFirst
and findUnique
more ergonomic with Effect by using Option
when it's nullable
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Replace
comes from https://github.com/sindresorhus/type-festThis is an older version of Effect so some changes may need to be made. The functions might have different arguments and the Effect.Effect type should have the arguments in reverse order (instead of R, E, A, it should be A, E, R)