nexus-prisma
nexus-prisma
is a Nexus plugin for bridging Prisma and Nexus. It extends the Nexus DSL t
with .model
and .crud
making it easy to project Prisma models and expose operations against them in your GraphQL API. Resolvers are dynamically created for you removing the need for traditional ORMs/query builders like TypeORM, Sequelize, or Knex. Out-of-box features include pagination, filtering, and ordering. When you do need to drop down into custom resolvers a Photon
instance on ctx
will be ready to serve you, the same great tool nexus-prisma
itself builds upon.
If you are still using nexus-prisma@0.3
/ Prisma 1 you can find the old docs here.
Contents
Installation
npm install nexus-prisma
Example
Given a Prisma schema like:
// schema.prisma
generator photonjs {
provider = "photonjs"
}
model User {
id String @id @unique @default(cuid())
email String @unique
birthDate DateTime
}
model Post {
id String @id @unique @default(cuid())
author User[]
}
You will be able to project these Prisma models onto your GraphQL API and expose operations against them:
import { queryType, mutationType, objectType } from 'nexus'
export const Query = queryType({
definition(t) {
t.crud.user()
t.crud.users({ ordering: true })
t.crud.post()
t.crud.posts({ filtering: true })
},
})
export const Mutation = mutationType({
definition(t) {
t.crud.createOneUser()
t.crud.createOnePost()
t.crud.deleteOneUser()
t.crud.deleteOnePost()
},
})
export const User = objectType({
name: 'User',
definition(t) {
t.model.id()
t.model.email()
t.model.birthDate()
t.model.posts()
},
})
export const Post = objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.author()
},
})
Generate your Photon.js database client:
prisma2 generate
Run your app:
ts-node --transpile-only src/main
import { GraphQLServer } from 'graphql-yoga'
import { makeSchema } from 'nexus'
import { nexusPrismaPlugin } from 'nexus-prisma'
import * as types from './types'
new GraphQLServer({
schema: makeSchema({
types,
plugins: [nexusPrismaPlugin()],
}),
}).start()
And get the resulting GraphQL API:
toggle me
scalar DateTime
input DateTimeFilter {
equals: DateTime
gt: DateTime
gte: DateTime
in: [DateTime!]
lt: DateTime
lte: DateTime
not: DateTime
notIn: [DateTime!]
}
type Mutation {
createOnePost(data: PostCreateInput!): Post!
createOneUser(data: UserCreateInput!): User!
deleteOnePost(where: PostWhereUniqueInput!): Post
deleteOneUser(where: UserWhereUniqueInput!): User
}
enum OrderByArg {
asc
desc
}
type Post {
author(
after: String
before: String
first: Int
last: Int
skip: Int
): [User!]!
id: ID!
}
input PostCreateInput {
author: UserCreateManyWithoutAuthorInput
id: ID
}
input PostCreateManyWithoutPostsInput {
connect: [PostWhereUniqueInput!]
create: [PostCreateWithoutAuthorInput!]
}
input PostCreateWithoutAuthorInput {
id: ID
}
input PostFilter {
every: PostWhereInput
none: PostWhereInput
some: PostWhereInput
}
input PostWhereInput {
AND: [PostWhereInput!]
author: UserFilter
id: StringFilter
NOT: [PostWhereInput!]
OR: [PostWhereInput!]
}
input PostWhereUniqueInput {
id: ID
}
type Query {
post(where: PostWhereUniqueInput!): Post
posts(
after: String
before: String
first: Int
last: Int
skip: Int
where: PostWhereInput
): [Post!]!
user(where: UserWhereUniqueInput!): User
users(
after: String
before: String
first: Int
last: Int
orderBy: UserOrderByInput
skip: Int
): [User!]!
}
input StringFilter {
contains: String
endsWith: String
equals: String
gt: String
gte: String
in: [String!]
lt: String
lte: String
not: String
notIn: [String!]
startsWith: String
}
type User {
birthDate: DateTime!
email: String!
id: ID!
posts(
after: String
before: String
first: Int
last: Int
skip: Int
): [Post!]!
}
input UserCreateInput {
birthDate: DateTime!
email: String!
id: ID
posts: PostCreateManyWithoutPostsInput
}
input UserCreateManyWithoutAuthorInput {
connect: [UserWhereUniqueInput!]
create: [UserCreateWithoutPostsInput!]
}
input UserCreateWithoutPostsInput {
birthDate: DateTime!
email: String!
id: ID
}
input UserFilter {
every: UserWhereInput
none: UserWhereInput
some: UserWhereInput
}
input UserOrderByInput {
birthDate: OrderByArg
email: OrderByArg
id: OrderByArg
}
input UserWhereInput {
AND: [UserWhereInput!]
birthDate: DateTimeFilter
email: StringFilter
id: StringFilter
NOT: [UserWhereInput!]
OR: [UserWhereInput!]
posts: PostFilter
}
input UserWhereUniqueInput {
email: String
id: ID
}
You can find a runnable version of this and other examples in the examples folder.
Guides
Recipes
Projecting Prisma Model Fields
Exposing one of your Prisma models in your GraphQL API
objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.title()
t.model.content()
},
})
Simple Computed GraphQL Fields
You can add computed fields to a GraphQL object using the standard GraphQL Nexus API.
objectType({
name: "Post",
definition(t) {
t.model.id()
t.model.title()
t.model.content()
t.string("uppercaseTitle", {
resolve({ title }, args, ctx) {
return title.toUpperCase(),
}
})
},
})
Complex Computed GraphQL Fields
If you need more complicated logic for your computed field (e.g. have access to some information from the database), you can use the photon
instance that's attached to the context and implement your resolver based on that.
objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.content()
t.string('anotherComputedField', {
async resolve(_parent, _args, ctx) {
const databaseInfo = await ctx.photon.someModel.someOperation(...)
const result = doSomething(databaseInfo)
return result
}
})
}
})
Project a Prisma Field to a Differently Named GraphQL Field
objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.content({ alias: 'body' })
},
})
Publish Full-Featured Reads on a Prisma Model
queryType({
definition(t) {
t.crud.post()
t.crud.posts({ ordering: true, filtering: true })
},
})
Publish Writes on a Prisma Model
queryType({
definition(t) {
t.crud.createPost()
t.crud.updatePost()
t.crud.updateManyPost()
t.crud.upsertPost()
t.crud.deletePost()
t.crud.deleteManyPost()
},
})
Publish Customized Reads on a Prisma Model
queryType({
definition(t) {
t.crud.posts({
filtering: { id: true, title: true },
ordering: { title: true },
})
},
})
Publish Model Writes Along Side Photon-Resolved Fields
mutationType({
definition(t) {
t.crud.createUser()
t.crud.updateUser()
t.crud.deleteUser()
t.crud.deletePost()
t.field('createDraft', {
type: 'Post',
args: {
title: stringArg(),
content: stringArg({ nullable: true }),
},
resolve: (parent, { title, content }, ctx) => {
return ctx.photon.posts.createPost({ title, content })
},
})
t.field('publish', {
type: 'Post',
nullable: true,
args: {
id: idArg(),
},
resolve(parent, { id }, ctx) {
return ctx.photon.posts.updatePost({
where: { id },
data: { published: true },
})
},
})
},
})
Reference
t.model
Only available within Nexus.objectType
definitions.
t.model
contains configurable field projectors that you use for projecting fields of your Prisma models onto your GraphQL Objects. The precise behaviour of field projectors vary by the Prisma type being projected. Refer to the respective sub-sections for details.
Model-Object Mapping
t.model
will either have field projectors for the Prisma model whose name matches that of the GraphQL Object
, or if the GraphQL Object
is of a name that does not match any of your Prisma models then t.model
becomes a function allowing you to specify the mapping, after which the field projectors become available.
Example
type User {
id: ID!
}
type Person {
id: ID!
}
objectType({
name: 'User',
definition(t) {
t.model.id()
},
})
objectType({
name: 'Person',
definition(t) {
t.model('User').id()
},
})
model User {
id String @id @default(cuid())
}
Enum
Auto-Projection
When a Prisma enum field is projected, the corresponding enum type will be automatically projected too (added to the GraphQL schema).
Member Customization
You can customize the projected enum members by defining the enum yourself in Nexus. nexus-prisma
will treat the name collision as an intent to override and so disable auto-projection.
Option Notes
Currently Prisma enums cannot be aliased (issue). They also cannot be type mapped since enum types cannot be mapped yet (issue).
Options
n/a
GraphQL Schema Contributions ?
type M {
MEF: E
}
enum E {
EV
}
Example
enum Mood {
HAPPY
SAD
CONFUSED
}
enum Role {
AUTHOR
EDITOR
}
type User {
role: Role
mood: Mood
}
enumType({
name: 'Role',
members: ['MEMBER', 'EDITOR'],
})
objectType({
name: 'User',
definition(t) {
t.model.role()
t.model.mood()
},
})
model User {
role Role
mood Mood
}
enum Mood {
HAPPY
SAD
COMFUSED
}
enum Role {
MEMBER
EDITOR
ADMIN
}
Scalar
Scalar Mapping
Prisma scalars are mapped to GraphQL scalars as follows:
Prisma GraphQL
------ -------
Boolean <> Boolean
String <> String
Int <> Int
Float <> Float
cuid() <> ID
DateTime <> DateTime (custom scalar)
uuid() <> UUID (custom scalar)
Auto-Projection
When a Prisma scalar is encountered that does not map to the standard GraphQL scalar types, it will be automatically projected (custom scalar added to the GraphQL schema). Examples include DateTime
and UUID
.
Option Notes
It is not possible to use type
because there is currently no way for a Prisma scalar to map to a differently named GraphQL scalar.
GraphQL Schema Contributions ?
type M {
MSF: S
}
scalar S
Options
alias
Example
type Post {
id: Int!
email: String!
scheduledPublish: DateTime
rating: Float!
active: Boolean!
}
scalar DateTime
objectType({
name: 'User',
definition(t) {
t.model.id()
t.model.email()
t.model.scheduledPublish()
t.model.rating()
t.model.active()
},
})
model User {
id String @id @default(cuid())
email String
scheduledPublish DateTime?
rating Float
active Boolean
}
Relation
Projecting relational fields only affects the current GraphQL object being defined. That is, the model that the field relates to is not auto-projected. This is a design choice intended to keep the nexus-prisma
system predictable for you. If you forget to project a relation you will receive feedback at build/boot time letting you know.
Options
type
alias
GraphQL Schema Contributions ?
type M {
MRF: RM
}
Example
type User {
latestPost: Post
}
objectType({
name: 'User',
definition(t) {
t.model.latestPost()
},
})
model User {
latestPost Post?
}
model Post {
title String
body String
}
List Enum
Like enums. It is not possible to order (issue) paginate (issue) or filter (issue) enum lists.
GraphQL Schema Contributions ?
type M {
MLEF: [E!]!
}
enum E {
EV
}
List Scalar
Like scalars. It is not possible to order (issue) paginate (issue) or filter (issue) scalar lists.
GraphQL Schema Contributions ?
type M {
MLSF: [S!]!
}
List Relation
Like relations but also supports batch related options.
Options
type
alias
filtering
pagination
ordering
GraphQL Schema Contributions ?
type M {
MLRF: [RM!]!
}
t.crud
Only available within GraphQL Query
and Mutation
definitions.
t.crud
contains configurable operation publishers that you use for exposing create, read, update, and delete mutations against your projected Prisma models.
There are 8 kinds of operations (reflecting a subset of Photon.js's capabilities). An operation publisher is the combination of some operation kind and a particular Prisma model. Thus the number of operation publishers on t.crud
is Prisma model count × operation kind count
. So for example if you defined 20 Prisma models then you would see 160 operation publishers on t.crud
.
Example
queryType({
definition(t) {
t.crud.user()
t.crud.users()
},
})
mutationType({
definition(t) {
t.crud.createOneUser()
t.crud.updateOneUser()
t.crud.upsertOneUser()
t.crud.deleteOneUser()
t.crud.updateManyUser()
t.crud.deleteManyUser()
},
})
model User {
...
}
Create
t.crud.createOne<M>
Allow clients to create one record at at time of the respective Prisma model.
Relation fields may be connected with an existing record or a sub-create may be inlined (generally referred to as nested mutations). If the relation is a List
then multiple connections or sub-creates are permitted.
Inlined creates are very similar to top-level ones but have the important difference that the sub-create has excluded the field where supplying its relation to the type of parent Object
being created would normally be. This is because a sub-create forces its record to relate to the parent one.
Underlying Photon Function
create
Options
type
alias
GraphQL Schema Contributions ?
mutation {
createOne_M(data: M_CreateInput): M!
}
input M_CreateInput {
MSF: S
MRF: RM_CreateManyWithout_M
}
input RM_CreateManyWithout_M {
connect: [RM_WhereUniqueInput!]
create: [RM_CreateWithout_M_Input!]
}
input RM_WhereUniqueInput {
RMF@unique: S
}
input RM_CreateWithout_M_Input = RM_CreateInput - RMRF: M
Example
mutation simple {
createOneUser(data: { email: "newton@prisma.io" }) {
id
}
}
mutation connectRelation {
createOneUser(
data: { email: "newton@prisma.io", posts: { connect: [1643] } }
) {
id
}
}
mutation createRelation {
createOneUser(
data: {
email: "newton@prisma.io"
posts: { create: [{ title: "On How The Prism Came To Be", body: "..." }] }
}
) {
id
posts {
title
}
}
}
type Mutation {
createOneUser(data: UserCreateInput!): User!
}
type Post {
author: User!
id: Int!
title: String!
body: String!
}
input PostCreateManyWithoutPostsInput {
connect: [PostWhereUniqueInput!]
create: [PostCreateWithoutAuthorInput!]
}
input PostCreateWithoutAuthorInput {
title: String!
body: String!
}
input PostWhereUniqueInput {
id: Int
title: String
}
type User {
email: String!
id: Int!
posts: [Post!]!
}
input UserCreateInput {
email: String!
posts: PostCreateManyWithoutPostsInput
}
mutationType({
definition(t) {
t.crud.createOneUser()
},
})
objectType({
name: 'User',
definition(t) {
t.model.id()
t.model.email()
t.model.posts()
},
})
objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.title()
t.model.body()
t.model.author()
},
})
model User {
id Int @id @unique
email String @unique
posts Post[]
}
model Post {
id Int @id
title String @unique
body String
author User
}
Read
t.crud.<M>
Allow clients to find one particular record of the respective Prisma model. They may search by any Prisma model field that has been marked with @unique
attribute.
The ability for list fields to be filtered, ordered, or paginated depends upon if those features have been enabled for those GraphQL objects via t.model.<ListRelation>
.
Underlying Photon Function
findOne
Options
type
alias
GraphQL Schema Contributions ?
mutation {
M(where: M_WhereUniqueInput): M!
}
input M_WhereUniqueInput {
MF: S
}
Example
query simple {
user(where: { email: "newton@prisma.io" }) {
id
}
}
type Query {
user(where: UserWhereUniqueInput!): User
}
type User {
id: Int!
email: String!
}
input UserWhereUniqueInput {
id: Int
email: String
}
queryType({
definition(t) {
t.user()
},
})
model User {
id Int @id @unique
email String @unique
}
Update
t.crud.updateOne<M>
Allow clients to update one particular record at a time of the respective Prisma model.
Underlying Photon Function
update
Options
type
alias
GraphQL Schema Contributions ?
mutation {
updateOne_M(data: M_UpdateInput!, where: M_WhereUniqueInput!): M
}
input M_WhereUniqueInput {
MF: S
}
input M_UpdateInput {
MSF: S
MRF: RM_UpdateManyWithout_M_Input
}
input RM_UpdateManyWithout_M_Input {
connect: [RM_WhereUniqueInput!]
create: [RM_CreateWithout_M_Input!]
delete: [RM_WhereUniqueInput!]
deleteMany: [RM_ScalarWhereInput!]
disconnect: [RM_WhereUniqueInput!]
set: [RM_WhereUniqueInput!]
update: [RM_UpdateWithWhereUniqueWithout_M_Input!]
updateMany: [RM_UpdateManyWithWhereNestedInput!]
upsert: [RM_UpsertWithWhereUniqueWithout_M_Input!]
}
input RM_WhereUniqueInput {}
input RM_CreateWithout_M_Input {}
input RM_UpdateWithWhereUniqueWithout_M_Input {
data: RM_UpdateWithout_M_DataInput!
where: RM_WhereUniqueInput!
}
input RM_UpdateWithout_M_DataInput {
RMSF: S
}
input RM_UpdateManyWithWhereNestedInput {
data: RM_UpdateManyDataInput!
where: RM_ScalarWhereInput!
}
input RM_UpsertWithWhereUniqueWithout_M_Input {
create: RM_CreateWithout_M_Input!
update: RM_UpdateWithout_M_DataInput!
where: RM_WhereUniqueInput!
}
For S_ScalarWhereInput
see batch filtering contributions.
Example
mutation simple {
updateOneUser(data: { email: "locke@prisma.io" }, where: { id: 1643 }) {
id
email
}
}
input IntFilter {
equals: Int
gt: Int
gte: Int
in: [Int!]
lt: Int
lte: Int
not: Int
notIn: [Int!]
}
type Mutation {
updateOneUser(data: UserUpdateInput!, where: UserWhereUniqueInput!): User
}
type Post {
author: User!
id: Int!
title: String!
}
input PostCreateWithoutAuthorInput {
body: String!
title: String!
}
input PostScalarWhereInput {
AND: [PostScalarWhereInput!]
body: StringFilter
id: IntFilter
NOT: [PostScalarWhereInput!]
OR: [PostScalarWhereInput!]
title: StringFilter
}
input PostUpdateManyDataInput {
body: String
id: Int
title: String
}
input PostUpdateManyWithoutAuthorInput {
connect: [PostWhereUniqueInput!]
create: [PostCreateWithoutAuthorInput!]
delete: [PostWhereUniqueInput!]
deleteMany: [PostScalarWhereInput!]
disconnect: [PostWhereUniqueInput!]
set: [PostWhereUniqueInput!]
update: [PostUpdateWithWhereUniqueWithoutAuthorInput!]
updateMany: [PostUpdateManyWithWhereNestedInput!]
upsert: [PostUpsertWithWhereUniqueWithoutAuthorInput!]
}
input PostUpdateManyWithWhereNestedInput {
data: PostUpdateManyDataInput!
where: PostScalarWhereInput!
}
input PostUpdateWithoutAuthorDataInput {
body: String
id: Int
title: String
}
input PostUpdateWithWhereUniqueWithoutAuthorInput {
data: PostUpdateWithoutAuthorDataInput!
where: PostWhereUniqueInput!
}
input PostUpsertWithWhereUniqueWithoutAuthorInput {
create: PostCreateWithoutAuthorInput!
update: PostUpdateWithoutAuthorDataInput!
where: PostWhereUniqueInput!
}
input PostWhereUniqueInput {
id: Int
title: String
}
type Query {
ok: Boolean!
}
input StringFilter {
contains: String
endsWith: String
equals: String
gt: String
gte: String
in: [String!]
lt: String
lte: String
not: String
notIn: [String!]
startsWith: String
}
type User {
email: String!
id: Int!
posts: [Post!]!
}
input UserUpdateInput {
email: String
id: Int
posts: PostUpdateManyWithoutAuthorInput
}
input UserWhereUniqueInput {
email: String
id: Int
}
mutationType({
definition(t) {
t.crud.updateOneUser()
},
})
objectType({
name: 'User',
definition(t) {
t.model.id()
t.model.email()
t.model.posts()
},
})
objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.title()
t.model.author()
},
})
model User {
id Int @id @unique
email String @unique
posts Post[]
}
model Post {
id Int @id
title String @unique
body String
author User
}
Upsert
t.crud.upsertOne<M>
Allow clients to update or create (aka. insert) one particular record at a time of the respective Prisma model. This operation is a combination of create and update. The generated GraphQL mutation matches data
and where
args to those of update, and create
to that of data
arg in create. Unlike update, upsert guarantees a return value.
Underlying Photon Function
upsert
Options
type
alias
GraphQL Schema Contributions ?
mutation {
upsertOne_M(
create: M_CreateInput!
data: M_UpdateInput!
where: M_WhereUniqueInput!
): M!
}
For M_UpdateInput
and M_WhereUniqueInput
see update contributions.
For M_CreateInput
see create contributions.
Example
Refer to update and create.
Delete
t.crud.deleteOne<M>
Allow clients to delete one particular record at a time of the respective Prisma model.
Underlying Photon Function
delete
Options
type
alias
GraphQL Schema Contributions ?
mutation {
deleteOne_M(where: M_WhereUniqueInput): M
}
input M_WhereUniqueInput {
MF@unique: S
}
Example
mutation simple {
deleteOneUser(where: { id: 1643 }) {
id
email
posts {
id
title
}
}
}
type Mutation {
deleteOneUser(where: UserWhereUniqueInput!): User
}
type Post {
author: User!
id: Int!
title: String!
}
type User {
email: String!
id: Int!
posts: [Post!]!
}
input UserWhereUniqueInput {
email: String
id: Int
}
mutationType({
definition(t) {
t.crud.deleteOneUser()
},
})
objectType({
name: 'User',
definition(t) {
t.model.id()
t.model.email()
t.model.posts()
},
})
objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.title()
t.model.author()
},
})
model User {
id Int @id @unique
email String @unique
posts Post[]
}
model Post {
id Int @id
title String @unique
body String
author User
}
Batch Read
t.crud.<M Pluralized>
Allow clients to fetch multiple records at once of the respective Prisma model.
Underlying Photon Function
findMany
Options
type
alias
filtering
pagiantion
ordering
GraphQL Schema Contributions ?
type Query {
M_s: [M!]!
}
Example
type Query {
users: [User!]!
}
type Post {
author: User!
id: Int!
title: String!
}
type User {
email: String!
id: ID!
posts: [Post!]!
}
queryType({
definition(t) {
t.users()
},
})
model User {
id Int @id @unique
email String @unique
posts Post[]
}
model Post {
id Int @id
title String @unique
body String
author User
}
Batch Update
t.crud.updateMany<M>
Allow clients to update multiple records of the respective Prisma model at once. Unlike update
nested relation-updating is not supported here. Clients get back a BatchPayload
object letting them know the number of affected records, but not access to the fields of affected records.
Underlying Photon Function
updateMany
Options
type
alias
GraphQL Schema Contributions ?
mutation {
updateMany_M(where: M_WhereInput, data: M_UpdateManyMutationInput): BatchPayload!
}
input M_UpdateManyMutationInput {
MSF: S
MEF: E
}
type BatchPayload {
count: Int!
}
For M_WhereInput
see batch filtering contributions.
Example
mutation updateManyUser(where: {...}, data: { status: ACTIVE }) {
count
}
See filtering option example. Differences are: operation semantics (update things); return type; data
arg.
Batch Delete
t.crud.deleteMany<M>
Allow clients to delete multiple records of the respective Prisma model at once. Clients get back a BatchPayload
object letting them know the number of affected records, but not access to the fields of affected records.
Underlying Photon Function
deleteMany
Options
type
alias
GraphQL Schema Contributions ?
mutation {
deleteMany_M(where: M_WhereInput): BatchPayload!
}
type BatchPayload {
count: Int!
}
For M_WhereInput
see filtering contribution.
Example
mutation {
deleteManyUser(where: {...}) {
count
}
}
See filtering option example. Differences are: operation semantics (delete things); return type.
Options
alias
undefined | String
Applies To
t.crud.<*>
t.model.<* - enum, list enum>
About
undefined
(default) By default Prisma model fields project onto GraphQL object fields of the same name.string
Change which GraphQL object field the Prisma model field projects onto.
GraphQL Schema Contributions ?
n/a
Example
type Post {
content: String!
}
objectType({
name: 'Post',
definition(t) {
t.model.body({ alias: 'content' })
},
})
model Post {
body String
}
type
undefined | String
Applies To
t.crud.<*>
t.model.<Relation>
t.model.<ListRelation>
About
-
undefined
(default) Point Prisma field to a GraphQL object whose name matches that of the Prisma field model type.
-
string
Point Prisma field to the given GraphQL object. This option can become necessary when you've have done model-object mapping and other Prisma models in your schema have relations to the name-mapped Prisma model. We are interested in developing further the model-object mapping API to automate this better (issue).
GraphQL Schema Contributions ?
n/a
Example
type Article {
title: String!
}
type User {
articles: [Article]
}
objectType({
name: 'Article',
definition(t) {
t.model('Post').id()
},
})
objectType({
name: 'User',
definition(t) {
t.model.posts({ alias: 'articles', type: 'Article' })
},
})
model User {
id String @id @default(cuid())
posts Post[]
}
model Post {
id String @id @default(cuid())
}
ordering
undefined | true | false | ModelWhitelist
Applies To
t.crud.<BatchRead>
t.model.<ListRelation>
About
Allow clients to order the records in a list field. Records can be ordered by their projected scalar fields in ascending or descending order. Ordering by fields on relations is not currently possible (issue).
undefined
(default) Like false
false
Disable orderingtrue
Enable ordering by all scalar fieldsModelWhitelist
(Record<string, true>
) Enable ordering by just Model scalar fields appearing in the given whitelist.
GraphQL Schema Contributions ?
M(orderBy: M_OrderByInput)
type M {
MF(orderBy: M_OrderByInput)
}
input M_OrderByInput {
MSF: OrderByArg
}
enum OrderByArg {
asc
desc
}
Example
query entrypointOrdering {
users(orderBy: { name: asc }) {
id
name
}
}
query relationOrdering {
user(where: { id: 1643 }) {
posts(orderBy: { title: dsc }) {
title
body
}
}
}
type Query {
user(where: UserWhereUniqueInput!): User
users(orderBy: UserOrderByInput): [User!]!
}
type Post {
body: String!
id: Int!
title: String!
}
type User {
id: Int!
name: String!
posts(orderBy: UserPostsOrderByInput): [Post!]!
}
input UserOrderByInput {
id: OrderByArg
name: OrderByArg
}
input UserPostsOrderByInput {
title: OrderByArg
}
input UserWhereUniqueInput {
id: Int
}
enum OrderByArg {
asc
desc
}
objectType({
name: 'Post',
definition(t) {
t.model.id()
t.model.title()
t.model.body()
},
})
objectType({
name: 'User',
definition(t) {
t.model.id()
t.model.name()
t.model.posts({ ordering: { title: true } })
},
})
queryType({
definition(t) {
t.crud.user()
t.crud.users({ ordering: true })
},
})
model User {
id Int @id
name String
posts Post[]
}
model Post {
id Int @id
title String
body String
}
undefined | true | false
Applies To
t.crud.<BatchRead>
t.model.<ListRelation>
About
undefined
(default) Like true
true
Enable paginationfalse
Disable pagination
GraphQL Schema Contributions
Ms(
after: String
before: String
first: Int
last: Int
skip: Int
)
type M {
RF(after: String, before: String, first: Int, last: Int, skip: Int)
}
Example
query batchRead {
users(skip: 50, first: 50) {
id
name
}
}
query batchReadRelation {
user(where: { id: 1643 }) {
posts(last: 10) {
title
body
}
}
}
...
objectType({
name: 'User',
definition(t) {
t.model.posts({ pagination: true })
},
})
queryType({
definition(t) {
t.crud.users({ pagination: true })
},
})
model User {
id Int @id
posts Post[]
// ...
}
model Post {
id Int @id
// ...
}
filtering
undefined | true | false | ModelWhitelist
Applies To
t.crud.<BatchRead>
t.model.<ListRelation>
About
undefined
(default) Like false
true
Enable filtering for all scalar fieldsfalse
Disable filteringModelWhitelist
(Record<string, true>
) Enable ordering by just Model scalar fields appearing in the given whitelist.
GraphQL Schema Contributions ?
See batch filtering contributions
Example
query batchReadFilter {
users(where: {
OR: [
{ age: { gt: 30 } },
posts: {
every: {
rating: {
lte: "0.5"
}
},
none: {
comments: {
none: {
author: {
status: BANNED
}
}
}
}
}
]
}) {
id
name
}
}
query batchReadRelationFilter {
users {
posts(where: { rating: { gte: 0.9 }}) {
comments {
content
}
}
}
}
type Comment {
author: User!
post: Post!
}
input CommentFilter {
every: CommentWhereInput
none: CommentWhereInput
some: CommentWhereInput
}
input CommentWhereInput {
AND: [CommentWhereInput!]
author: UserWhereInput
content: StringFilter
id: StringFilter
NOT: [CommentWhereInput!]
OR: [CommentWhereInput!]
post: PostWhereInput
}
input FloatFilter {
equals: Float
gt: Float
gte: Float
in: [Float!]
lt: Float
lte: Float
not: Float
notIn: [Float!]
}
input IntFilter {
equals: Int
gt: Int
gte: Int
in: [Int!]
lt: Int
lte: Int
not: Int
notIn: [Int!]
}
type Post {
author: User!
comments(
after: String
before: String
first: Int
last: Int
skip: Int
): [Comment!]!
rating: Float!
}
input PostFilter {
every: PostWhereInput
none: PostWhereInput
some: PostWhereInput
}
input PostWhereInput {
AND: [PostWhereInput!]
author: UserWhereInput
comments: CommentFilter
id: StringFilter
NOT: [PostWhereInput!]
OR: [PostWhereInput!]
rating: FloatFilter
}
type Query {
user(where: UserWhereUniqueInput!): User
users(
after: String
before: String
first: Int
last: Int
skip: Int
where: UserWhereInput
): [User!]!
}
input StringFilter {
contains: String
endsWith: String
equals: String
gt: String
gte: String
in: [String!]
lt: String
lte: String
not: String
notIn: [String!]
startsWith: String
}
type User {
age: Int!
}
enum UserStatus {
ACTIVE
BANNED
}
input UserWhereInput {
age: IntFilter
AND: [UserWhereInput!]
comments: CommentFilter
id: StringFilter
NOT: [UserWhereInput!]
OR: [UserWhereInput!]
posts: PostFilter
status: UserStatus
}
input UserWhereUniqueInput {
id: ID
}
objectType({
name: 'User',
definition(t) {
t.model.age()
},
})
objectType({
name: 'Post',
definition(t) {
t.model.author()
t.model.rating()
t.model.comments()
},
})
objectType({
name: 'Comment',
definition(t) {
t.model.author()
t.model.post()
},
})
queryType({
definition(t) {
t.crud.users({ filtering: true })
t.crud.user()
},
})
model User {
id String @id @unique @default(cuid())
posts Post[]
age Int
status UserStatus
}
model Post {
id String @id @unique @default(cuid())
author User
comments Comment[]
rating Float
}
model Comment {
id String @id @unique @default(cuid())
author User
post Post
content String
}
enum UserStatus {
BANNED
ACTIVE
}
GraphQL Schema Contributions
How to Read
M = model F = field L = list S = scalar R = relation E = enum V = value
Batch Filtering
Sources
query {
Ms(where: M_WhereInput, ...): [M!]!
}
mutation {
updateMany_M(where: M_WhereInput, ...) BatchPayload!
deleteMany_M(where: M_WhereInput): BatchPayload!
}
type M {
MRF: RM(where: RM_WhereInput): [RM!]!
}
Where
input M_WhereInput {
AND: [M_WhereInput!]
NOT: [M_WhereInput!]
OR: [M_WhereInput!]
MSF: S_Filter
MRF: RM_Filter
}
input RM_Filter {
every: RM_WhereInput
none: RM_WhereInput
some: RM_WhereInput
}
input RM_ScalarWhereInput {
AND: [RM_ScalarWhereInput!]
NOT: [RM_ScalarWhereInput!]
OR: [RM_ScalarWhereInput!]
RMSF: S_Filter
}
Scalar Filters
ID
scalars use StringFilter
(issue). We are considering a tailored DateTime
filter (issue).
input BooleanFilter {
equals: Boolean
not: Boolean
}
input IntFilter {
equals: S
gt: S
gte: S
in: [S!]
lt: S
lte: S
not: S
notIn: [S!]
}
input FloatFilter {}
input DateTimeFilter {}
input StringFilter {
contains: String
endsWith: String
equals: String
gt: String
gte: String
in: [String!]
lt: String
lte: String
not: String
notIn: [String!]
startsWith: String
}
input UUIDFilter {}
System Behaviours
Null-Free Lists
Projection for Prisma list types always project as a fully non-nullable GraphQL type. This is because Prisma list fields (and list member type) can themselves never be null, and because Prisma does not support @default
on list types.
For consistency we also apply the same pattern for t.crud.<BatchRead>
.
type Query {
users: [User!]!
}
type User {
posts: [Post!]!
}
queryType({
definition(t) {
t.crud.users()
},
})
objectType({
name: 'User',
definition(t) {
t.crud.posts()
},
})
model User {
posts Post[]
}
Workflow
Configuration
In most cases you should not need to configure anything. If you do, and you don't feel like it is an edge-case, we'd like to know about it. Our goal is that for vast majority of cases nexus-prisma be zero-config.
type Options = {
photon?: (ctx: Nexus.core.GetGen<'context'>) => Photon
shouldGenerateArtifacts?: boolean
inputs?: {
photon?: string
}
outputs?: {
typegen?: string
}
}
Usage
- Import
nexusPrismaPlugin
from nexus-prisma
- Create and configure it if needed (shouldn't be)
- Pass into
Nexus.makeSchema
plugins
array
Example
import { nexusPrismaPlugin } from 'nexus-prisma'
import { makeSchema } from 'nexus'
import * as types from './types'
const schema = makeSchema({ types, plugins: [nexusPrismaPlugin()] })
Project Setup
These are tips to help you with a successful project workflow
-
Keep app schema somewhere apart from server so that you can do ts-node --transpile-only path/to/schema/module
to generate typegen. This will come in handy in certain deployment contexts.
-
Consider using something like the following set of npm scripts. The postinstall
step is helpful for guarding against pruning since the generated @types
packages will be seen as extraneous. We have an idea to solve this with package facades. For yarn users though this would still be helpful since yarn rebuilds all packages whenever the dependency tree changes in any way (issue).
{
"scripts": {
"generate:prisma": "prisma2 generate",
"generate:nexus": "ts-node --transpile-only path/to/schema/module",
"generate": "npm -s run generate:prisma && npm -s run generate:nexus",
"postinstall": "npm -s run generate"
}
}
-
In your deployment pipeline you may wish to run a build step. Heroku buildpacks for example call npm run build
if that script is defined in your package.json
. If this is your case and you are a TypeScript user consider a build setup as follows. Prior to tsc
we run artifact generation so that TypeScript will have types for the all the resolver signatures etc. of your app. In turn to ensure artifact generation runs we declare the environment variable as such. Artifact generation toggling based on NODE_ENV
value is often sufficient but not always. For example in a deployment pipeline NODE_ENV
may be set to "production" (it is with Heroku).
{
"scripts": {
"build": "NEXUS_SHOULD_GENERATE_ARTIFACTS=true npm -s run generate && tsc"
}
}
Links