Prisma is an easy to use ORM with really useful Typescript types that change the result type of a query based on the options you pass in.
While building out an application, you'll undoubtedly run into a situation where you want to wrap an existing Prisma method with custom logic. You may decide to pass-through the types so that the caller can decide if they want to extend query.
If you're using Prisma's query arguments, you'll also expect to get Prisma's return types.
This is not as easy as it sounds, as simply passing the data (or even passing a generic backed value) does not work as intended.
You may start by trying something like this. Wrapping the todo findMany
call with a function that accepts Prisma query args, but modifies the query to do something specific.
// DOES NOT WORK
specialFindMany<T extends Prisma.TodoFindManyArgs>(args?:T){
return prisma.todo.findMany({
...args,
where:{
...args?.where
isSpecial: true
}
})
}
This will produce type errors.
Discovery
As with anything in Typescript, if you dig into the types, you can get at what a function actually expects.
Looking at the types for todo.findMany()
as of Prisma 2.24.0
you'll find something that looks like this:
findMany<T extends TodoFindManyArgs>(
args?: SelectSubset<T, TodoFindManyArgs>
): CheckSelect<T, PrismaPromise<Array<Todo>>, PrismaPromise<Array<TodoGetPayload<T>>>>
You can see that they're pulling the type from SelectSubset
s generics.
Solution
If you copy this type into both your method's declaration as well as the prisma.finyMany
generic, you'll get a working typed passthrough.
async specialFindMany<T extends Prisma.TodoFindManyArgs>(
args?: Prisma.SelectSubset<T, Prisma.TodoFindManyArgs>,
) ){
// Other custom logic
const result = await prisma.todo.findMany<
Prisma.SelectSubset<T, Prisma.SearchFindManyArgs>>(
{
...args!,
where:{
...args?.where
isSpecial: true
}
})
// Other custom logic
return result
}
Without passing in the generic, the types will still fail to pass through (at least in my testing).
Since args is optional, the types will complain about forcing an object through. There may be a better solution to this, but I simply !
forced the args type to exist.
Conclusion
This feels pretty cumbersome, and I'd love to see some more flexible typing in Prisma, but it gets the job done for wrapping database calls.
This solution unfortunately doesn't cover extending the types that go into the Prisma call, so including include
or select
will not result in proper output types.
If you have a better solution, please let me know in the comments!!!
Top comments (3)
Hi, there's some typo I guess
where:{
...args?.where //here, it says: " ',' expected "
isSpecial: true
Anyway thank you for your article, I have similar problem, and now I know where do dig further.
Update:
I have figured out how to write a wrapper for my needs.
Mayme I deed something very stupid but it works the way I need. It checks the type for my wrapper and this type is being updated when I update the schema (you can see it on the picture here dev-to-uploads.s3.amazonaws.com/up...).
There's more, I went further and hid whole prisma data structure as well to make my life easier If I decide to change the ORM:
//Here's just an id and optional object for columns you want to be fetched
getOne(1, {cart_items: true})
function getOne(theId: number, columns?: Prisma.cartsSelect){
const cartId = {id: theId}
const allCarts = await prisma.carts.findUnique({
where: cartId,
select: columns
})
great 👌