-
-
Notifications
You must be signed in to change notification settings - Fork 165
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
504 additions
and
0 deletions.
There are no files selected for viewing
125 changes: 125 additions & 0 deletions
125
website/content/docs/plugins/prisma/indirect-relations.mdx
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
--- | ||
title: Indirect relations | ||
description: Indirect relations and join tables | ||
--- | ||
## Selecting fields from a nested GraphQL field | ||
|
||
By default, the `nestedSelection` function will return selections based on the type of the current | ||
field. `nestedSelection` can also be used to get a selection from a field nested deeper inside other | ||
fields. This is useful if the field returns a type that is not a `prismaObject`, but a field nested | ||
inside the returned type is. | ||
|
||
```typescript | ||
const PostRef = builder.prismaObject('Post', { | ||
fields: (t) => ({ | ||
title: t.exposeString('title'), | ||
content: t.exposeString('content'), | ||
author: t.relation('author'), | ||
}), | ||
}); | ||
|
||
const PostPreview = builder.objectRef<Post>('PostPreview').implement({ | ||
fields: (t) => ({ | ||
post: t.field({ | ||
type: PostRef, | ||
resolve: (post) => post, | ||
}), | ||
preview: t.string({ | ||
nullable: true, | ||
resolve: (post) => post.content?.slice(10), | ||
}), | ||
}), | ||
}); | ||
|
||
builder.prismaObject('User', { | ||
fields: (t) => ({ | ||
id: t.exposeID('id'), | ||
postPreviews: t.field({ | ||
select: (args, ctx, nestedSelection) => ({ | ||
posts: nestedSelection( | ||
{ | ||
// limit the number of postPreviews to load | ||
take: 2, | ||
}, | ||
// Look at the selections in postPreviews.post to determine what relations/fields to select | ||
['post'], | ||
// (optional) If the field returns a union or interface, you can pass a typeName to get selections for a specific object type | ||
'Post', | ||
), | ||
}), | ||
type: [PostPreview], | ||
resolve: (user) => user.posts, | ||
}), | ||
}), | ||
}); | ||
``` | ||
|
||
## Indirect relations (eg. Join tables) | ||
|
||
If you want to define a GraphQL field that directly exposes data from a nested relationship (many to | ||
many relations using a custom join table is a common example of this) you can use the | ||
`nestedSelection` function passed to `select`. | ||
|
||
Given a prisma schema like the following: | ||
|
||
``` | ||
model Post { | ||
id Int @id @default(autoincrement()) | ||
title String | ||
content String | ||
media PostMedia[] | ||
} | ||
model Media { | ||
id Int @id @default(autoincrement()) | ||
url String | ||
posts PostMedia[] | ||
uploadedBy User @relation(fields: [uploadedById], references: [id]) | ||
uploadedById Int | ||
} | ||
model PostMedia { | ||
id Int @id @default(autoincrement()) | ||
post Post @relation(fields: [postId], references: [id]) | ||
media Media @relation(fields: [mediaId], references: [id]) | ||
postId Int | ||
mediaId Int | ||
} | ||
``` | ||
|
||
You can define a media field that can pre-load the correct relations based on the graphql query: | ||
|
||
```typescript | ||
const PostDraft = builder.prismaObject('Post', { | ||
fields: (t) => ({ | ||
title: t.exposeString('title'), | ||
media: t.field({ | ||
select: (args, ctx, nestedSelection) => ({ | ||
media: { | ||
select: { | ||
// This will look at what fields are queried on Media | ||
// and automatically select uploadedBy if that relation is requested | ||
media: nestedSelection( | ||
// This arument is the default query for the media relation | ||
// It could be something like: `{ select: { id: true } }` instead | ||
true, | ||
), | ||
}, | ||
}, | ||
}), | ||
type: [Media], | ||
resolve: (post) => post.media.map(({ media }) => media), | ||
}), | ||
}), | ||
}); | ||
|
||
const Media = builder.prismaObject('Media', { | ||
select: { | ||
id: true, | ||
}, | ||
fields: (t) => ({ | ||
url: t.exposeString('url'), | ||
uploadedBy: t.relation('uploadedBy'), | ||
}), | ||
}); | ||
``` |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
--- | ||
title: Interfaces | ||
description: Creating interfaces for prisma models that can be shared by variants | ||
--- | ||
|
||
`builder.prismaInterface` works just like builder.prismaObject and can be used to define either the | ||
primary type or a variant for a model. | ||
|
||
The following example creates a `User` interface, and 2 variants Admin and Member. The `resolveType` | ||
method returns the typenames as strings to avoid issues with circular references. | ||
|
||
```typescript | ||
builder.prismaInterface('User', { | ||
name: 'User', | ||
fields: (t) => ({ | ||
id: t.exposeID('id'), | ||
email: t.exposeString('email'), | ||
}), | ||
resolveType: (user) => { | ||
return user.isAdmin ? 'Admin' : 'Member'; | ||
}, | ||
}); | ||
|
||
builder.prismaObject('User', { | ||
variant: 'Admin', | ||
interfaces: [User], | ||
fields: (t) => ({ | ||
isAdmin: t.exposeBoolean('isAdmin'), | ||
}), | ||
}); | ||
|
||
builder.prismaObject('User', { | ||
variant: 'Member', | ||
interfaces: [User], | ||
fields: (t) => ({ | ||
bio: t.exposeString('bio'), | ||
}), | ||
}); | ||
``` | ||
|
||
When using select mode, it's recommended to add selections to both the interface and the object | ||
types that implement them. Selections are not inherited and will fallback to the default selection | ||
which includes all scalar columns. | ||
|
||
You will not be able to extend an interface for a different prisma model, doing so will result in an | ||
error at build time. |
This file contains 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
This file contains 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
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,3 +59,4 @@ builder.prismaNode('Post', { | |
}), | ||
}); | ||
``` | ||
|
Oops, something went wrong.