Is your feature request related to a problem? Please describe.
As described in e.g. #3104, the current Prisma client API is quite limited when it comes to more complex relational queries (i.e. you can just query exactly one level of nodes). As a fallback it's possible to use the $fragment API which however can feel a bit cumbersome and lacks better tooling to make it nice to use (e.g. auto-completion for the GraphQL query/fragment + auto-generated interfaces for the response type generics)
Describe the solution you'd like
I'm proposing to add the following API to allow for more complex, type-safe, relational queries using the given programming language instead of having to fallback to GraphQL. This allows you to additionally to the user also deeply fetch the user's friends and posts including their comments.
const dynamicResult: DynamicResult = await prisma
.user('bobs-id')
.$nested({ posts: { comments: true }, friends: true })
// assuming the following type definitions
type Post = {
id: string
title: string
body: string
}
type Comment = {
id: string
text: string
}
type User = {
id: string
name: string
}
// Note: This is just here for the sake of demonstration and would be automatically
// derived based on conditional types in TS
type DynamicResult = {
posts: (Post & { comments: Comment[] })[]
friends: User[]
}
The idea is to mimic a GraphQL query in JS (or languages):
true or false. Empty/nested objects are equivalent to specifying true.true by default Similar to how the client automatically fetches all scalar fields, the queried relations inside the $nested query also contain all scalar values by default.false by default Also same as for the typically client behavior, relations are not fetched by default but can be fetched by setting the corresponding field to true or { ... }.The $nested API works both on the root level (e.g. prisma.$nested({ ... })) as well on a node level (e.g. prisma.users().$nested({ ... }). See full example:
const nestedResult = await prisma.$nested({
users: {
$args: { first: 100 },
firstName: false,
posts: {
comments: true,
},
friends: true,
},
})
One thing I'm still missing in this proposal compared to how prisma-binding works now is how to use the info parameter from the GraphQL resolver to automatically fetch only the relations that are used in the query.
query {
events {
id
venue {
id
}
}
}
The above would be a query I sent to my Apollo Server. In the resolver, I then would use prisma.events(). In this case, I also want it to fetch the venue relation.
However, if venue { id } would not be a part of the query I don't want it to fetch that relation.
With your proposal I don't see how that situation would be fixed, but I could be wrong of course 馃槃. Or maybe this would be out of scope for the prisma-client and there should be a separate package that translates the info param to the $nested syntax in your proposal?
Thanks for bringing this up. Prisma client is GraphQL agnostic by design. However, we're currently working on some really exciting features/improvements that let you build GraphQL servers with a similar efficiency and minimal boilerplate while keeping all the benefits of Prisma clients.
More on this soon. Please let's keep this issue focussed on the proposed API design and related topics.
this is basically populate of mongoose, but much more powerful
the problem is how to make it fast
when we moved to GraphQL we just avoid populate as it makes harder to make it type safe
@schickling looking at objection I am a bit surprised they've got the same-I've never visited this corner or the docs, even when working daily with it: http://vincit.github.io/objection.js/#relationexpression-object-notation
so you're on the same track. Love that.
Just one question on dynamic relation nesting-are you sure you won't need:
{
parent: {
$recursive: 5
}
}
// equals
{
parent: {
parent: {
parent: {
parent: {
parent: true
}
}
}
}
}
or maybe
{
parent: {
$recursive: 'all'
}
}
?
@capaj maybe a better name would be $depth?
The idea is to mimic a GraphQL query in JS (or languages):
@schickling Like that premise.
Somewhere in docs should be noted that related queries can be resolved with this:
I was using string fragment workaround until I realized, that TypeScript interfaces for graphqlgen should be flat and missing non-scalar fields like creator (User) etc. should be resolved elsewhere.
import { WebResolvers } from '../generated/graphqlgen';
export const Web: WebResolvers.Type = {
...WebResolvers.defaultResolvers,
creator: (parent, _, ctx) => {
return ctx.db.web({ id: parent.id }).creator();
},
};
So $nested use case should be better explained IMHO.
Update: Maybe it's wrong, I don't know.
I would also love a tool that would be able to translate Apollo's GraphQLResolveInfo object "info" into these nested queries.
I see that the $fragment function now takes a DocumentNode object in addition to a string, but I have no idea how to use DocumentNode in this case.
The previous prisma-binding worked with the info object out of the box, and that was great
Any update on this? Is it planned for anytime soon?
@vjsingh I fear this FAQ entry is relevant
Most helpful comment
One thing I'm still missing in this proposal compared to how
prisma-bindingworks now is how to use theinfoparameter from the GraphQL resolver to automatically fetch only the relations that are used in the query.The above would be a query I sent to my Apollo Server. In the resolver, I then would use
prisma.events(). In this case, I also want it to fetch thevenuerelation.However, if
venue { id }would not be a part of the query I don't want it to fetch that relation.With your proposal I don't see how that situation would be fixed, but I could be wrong of course 馃槃. Or maybe this would be out of scope for the prisma-client and there should be a separate package that translates the
infoparam to the$nestedsyntax in your proposal?