Prisma plugin for GraphQL Nexus, a code first GraphQL schema construction library
The nexus-prisma plugin provides CRUD building blocks based on the Prisma datamodel. When implementing your GraphQL server, you build upon these building blocks and expose/customize them to your own API needs.
When using nexus-prisma, you're using a code-first (instead of an SDL-first) approach for GraphQL server development. Read more about the benefits of code-first in this article series:
- The Problems of "Schema-First" GraphQL Server Development
- Introducing GraphQL Nexus: Code-First GraphQL Server Development
- Using GraphQL Nexus with a Database
You can also check out a quick demo on CodeSandbox:
- CRUD operations for your Prisma models in GraphQL (easier than
forwardTo) - Customize your Prisma models, e.g. hide certain fields or add computed fields
- Full type-safety: Coherent set of types for GraphQL schema and database
- Compatible with the GraphQL ecosystem (e.g.
apollo-server,graphql-yoga, ...) - Incrementally adoptable
- Compatible both with TypeScript and JavaScript
You can find the docs for nexus-prisma here. The docs also include a Getting started-section.
Here's a minimal example for using nexus-prisma:
Prisma datamodel:
type Todo {
id: ID! @unique
title: String!
done: Boolean! @default(value: "false")
}GraphQL server code::
import { prismaObjectType } from 'nexus-prisma'
import { idArg } from 'nexus'
// Expose the full "Query" building block
const Query = prismaObjectType({
name: 'Query',
// Expose all generated `Todo`-queries
definition: t => t.prismaFields(['*'])
})
// Customize the "Mutation" building block
const Mutation = prismaObjectType({
name: 'Mutation',
definition(t) {
// Keep only the `createTodo` mutation
t.prismaFields(['createTodo'])
// Add a custom `markAsDone` mutation
t.field('markAsDone', {
args: { id: idArg() },
nullable: true,
resolve: (_, { id }, ctx) {
return ctx.prisma.updateTodo({
where: { id },
data: { done: true }
})
}
})
}
})
const schema = makePrismaSchema({
types: [Query, Mutation],
// More config stuff, e.g. where to put the generated SDL
})
// Feed the `schema` into your GraphQL server, e.g. `apollo-server, `graphql-yoga` Expand to view the generated SDL for the final GraphQL API
# The fully exposed "Query" building block
type Query {
todo(where: TodoWhereUniqueInput!): Todo
todoes(after: String, before: String, first: Int, last: Int, orderBy: TodoOrderByInput, skip: Int, where: TodoWhereInput): [Todo!]!
todoesConnection(after: String, before: String, first: Int, last: Int, orderBy: TodoOrderByInput, skip: Int, where: TodoWhereInput): TodoConnection!
}
# The customized "Mutation" building block
type Mutation {
createTodo(data: TodoCreateInput!): Todo!
markAsDone(id: ID): Todo
}
# The Prisma model
type Todo {
done: Boolean!
id: ID!
title: String!
}
# More of the generated building blocks:
# e.g. `TodoWhereUniqueInput`, `TodoCreateInput`, `TodoConnection`, ...You can find some easy-to-run example projects based on nexus-prisma in the prisma-examples:
- GraphQL: Simple setup keeping the entire schema in a single file.
- GraphQL + Auth: Advanced setup including authentication and authorization and a modularized schema.
The nexus-prisma plugin is the glue between the Prisma client and GraphQL Nexus. It generates CRUD building blocks based for your Prisma models.
When constructing your GraphQL schema with GraphQL Nexus, you build upon these building blocks and expose/customize them to your own API needs.
Assume you have a User type in your Prisma datamodel. nexus-prisma-generate will generate the following building blocks for it:
-
Queries
user(...): User!: Fetches a single recordusers(...): [User!]!: Fetches a list of recordsusersConnection(...): UserConnection!: Relay connections & aggregations
-
Mutations
createUser(...): User!: Creates a new recordupdateUser(...): User: Updates a recorddeleteUser(...): User: Deletes a recordupdatesManyUsers(...): BatchPayload!: Updates many records in bulkdeleteManyUsers(...): BatchPayload!: Deletes many records in bulk
-
UserCreateInput: Wraps all fields of the recordUserUpdateInput: Wraps all fields of the recordUserWhereInput: Provides filters for all fields of the recordUserWhereUniqueInput: Provides filters for unique fields of the recordUserUpdateManyMutationInput: Wraps fields that can be updated in bulkUserOrderByInput: Specifies ascending or descending orders by field
UserCreateInputandUserUpdateInputdiffer in the way relation fields are treated.
Install dependencies:
npm install --save nexus-prismaOther required dependencies:
npm install --save nexus graphql prisma-client-lib
The CRUD building blocks are generated using the nexus-prisma-generate CLI:
npx nexus-prisma-generate --output ./src/generated/nexus-prismaIt is recommended to add this command as a post-deploy hook to your prisma.yml, e.g.:
hooks:
post-deploy:
- prisma generate
- npx nexus-prisma-generate --output ./src/generated/nexus-prisma # Runs the codegen tool from nexus-prismaprismaObjectType is a wrapper around Nexus' objectType. It provides two additional methods to the model: prismaType() and prismaFields(). These two methods simplify the coupling between a Prisma schema and a Nexus schema and provide a straightforward mechanism to customize the Prisma models, fields, and input-arguments which are included in the Nexus schema.
It expects an object with the following properties:
name(string): The name of the Prisma model or generated CRUD GraphQL type you want to expose in your API, e.g.Query,Mutation,User,Todo,UserWhereUniqueInput,TodoConnection, ...definition(t) => {}(function): A function to customize the Prisma model or generated CRUD GraphQL typet. To expose the entire type, call:t.prismaFields(['*']). See the documentation ofprismaFields()below for more info.
nonNullDefaults(boolean or object): Specifies whether the nullability behaviour for field arguments and field types. All input arguments and return types of fields are non-null by default. If you want the behaviour to differ for input arguments and field (outout) types, you can pass an object with these properties:input(boolean): Specifies whether input arguments should be required. Default:true.output(boolean): Specifies whether return values of fields should be required. Default:true.
description: A string that shows up in the generated SDL schema definition to describe the type. It is also picked up by tools like the GraphQL Playground or graphiql.defaultResolver
prismaExtendType wraps the Nexus extendType function and adds two utility methods to the model t: prismaFields() and prismaType(). Like extendType, prismaExtendType is primarily useful in incrementally defining the fields of a type (i.e. defining the fields of a type from multiple locations within a project). Such type extension is commonly used to co-locate (within in a single file) type definitions for a specific domain with relevant additions to the root Query and Mutation types.
It expects an object with the following properties:
type(string): The name of the Prisma model or generated CRUD GraphQL type you want to augment with additional fields.definition(t) => {}(function): A function to customize the Prisma model or generated CRUD GraphQL typetby adding new fields to the specifiedtype. The type of the argumenttmatches its analog inprismaObjectType.
prismaFields() is called on the type t that's passed into the definition function. All the fields exposed using prismaFields() are automatically resolved. The prismaFields() function expects an array of Prisma fields where each field can either be provided:
- as a simple string to indicate that it should be exposed in the same way it was defined in the datamodel
- as a configuration object in case you want to rename the field or adjust its arguments
/**
* Pick, or customize the fields of the underlying object type
*/
t.prismaFields(fieldsToExpose: string[] | Field[])
/**
* Pick, or customize the fields of the underlying object type
* (Equivalent to the above)
*/
t.prismaFields({ pick: string[] | Field[] })
/**
* Filter or customize the fields of the underlying object type
*/
t.prismaFields({ filter: (string[] | Field[]) | (fields: string[]) => string[] })
interface Field {
name: string // Name of the field you want to expose
alias: string // Name of the alias of you want to give the field
args: string[] // Arguments of the field you want to expose
}Expose all fields
const User = prismaObjectType({
name: 'User',
definition(t) {
t.prismaFields(['*'])
},
})Expose only the id and name field
const User = prismaObjectType({
name: 'User',
definition(t) {
t.prismaFields(['id', 'name'])
},
})or
const User = prismaObjectType({
name: 'User',
definition(t) {
t.prismaFields({ pick: ['id', 'name'] })
},
})Expose all fields but the id and name
const User = prismaObjectType({
name: 'User',
definition(t) {
t.prismaFields({ filter: ['id', 'name'] })
},
})Expose only the users field, and renames it to customers
const Query = prismaObjectType({
name: 'Query',
definition(t) {
t.prismaFields([{ name: 'users', alias: 'customers' }])
},
})Expose only the users field, and only the first and last args
const Query = prismaObjectType({
name: 'Query',
definition(t) {
t.prismaFields([{ name: 'users', args: ['first', 'last'] }])
},
})Contains all the options to use native nexus default methods with nexus-prisma generated schema.
Pass in all the options as-is
const Query = prismaObjectType({
name: 'Query',
definition(t) {
t.field('users', t.prismaType.users)
},
})Use all the options, but override the resolver
const Query = prismaObjectType({
name: 'Query',
definition(t) {
t.field('users', {
...t.prismaType.users,
resolve(root, args, ctx) {
// Custom implementation
},
})
},
})Use all the options, add more arguments with a custom resolver
const Query = prismaObjectType({
name: 'Query',
definition(t) {
t.field('users', {
...t.prismaType.users,
args: {
...t.prismaType.users.args,
newArg: stringArg(),
},
resolve(root, args, ctx) {
// Custom implementation
},
})
},
})By default, nexus will infer the root types from your schema. In some cases, you might need the roots to be the actual types return by the prisma-client (eg: You want to use a hidden field from your Prisma datamodel to expose a computed one)
In that case, you need to add the prisma-client types to the typegenAutoConfig.sources config:
import { join } from 'path'
import { makePrismaSchema } from 'nexus-prisma'
const schema = makePrismaSchema({
// ... other configs,
typegenAutoConfig: {
sources: [
{
source: path.join(__dirname, './relative/path/to/prisma/client'),
alias: 'prisma',
},
],
},
})nexus will match the types name of your schema with the TS interfaces contained in the prisma-client file, and use these types instead of the inferred one from your schema. If needed, you can also input your own types.
