Plugins / Prisma

Overview

About

This plugin integrates Prisma into Nexus. It gives you an API to project fields from models defined in your Prisma schema into your GraphQL API. It also gives you an API to build GraphQL root fields that allow your API clients to query and mutate data.

Note: You may also use nexus-prisma, a newer API for integrating Nexus and Prisma.

Installation

$npm install nexus-plugin-prisma @prisma/client
$npm install -D prisma

Usage

  1. Import nexusPrisma from nexus-plugin-prisma
  2. Create and configure it if needed (usually not)
  3. Pass into Nexus.makeSchema plugins array

Note: If you're looking for CRUD capabilities, you must enable the experimentalCRUD option.

You can find runnable examples in the repo examples folder.

Example

1import { nexusPrisma } from 'nexus-plugin-prisma'
2import { makeSchema } from 'nexus'
3import * as types from './types'
4
5const schema = makeSchema({
6 types,
7 plugins: [nexusPrisma()],
8})

Configuration

Note that, in most cases, you should not need to configure anything.

1type Options = {
2 /**
3 * Enable experimental CRUD capabilities.
4 * Add a `t.crud` method in your definition block to generate CRUD resolvers in your `Query` and `Mutation` GraphQL Object Type.
5 *
6 * @default false
7 */
8
9 experimentalCRUD?: boolean
10 /**
11 * nexus-plugin-prisma will call this to get a reference to an instance of the Prisma Client.
12 * The function is passed the context object. Typically a Prisma Client instance will
13 * be available on the context to support your custom resolvers. Therefore the
14 * default getter returns `ctx.prisma`.
15 */
16 prismaClient?: PrismaClientFetcher
17 /**
18 * Same purpose as for that used in `NexusSchema.makeSchema`. Follows the same rules
19 * and permits the same environment variables.
20 */
21 shouldGenerateArtifacts?: boolean
22
23 inputs?: {
24 /**
25 * What is the path to the Prisma Client package? By default looks in
26 * `node_modules/@prisma/client`. This is needed in order to read your Prisma
27 * schema AST and Prisma Client CRUD info from the generated Prisma Client package.
28 */
29 prismaClient?: string
30 }
31 outputs?: {
32 /**
33 * Where should typegen be put on disk? By default emits into `node_modules/@types`.
34 */
35 typegen?: string
36 }
37 computedInputs?: GlobalComputedInputs
38
39 /**
40 * GraphQL doesn't support union types. The plugin has to apply a flattening heuristic and pick by default the broadest member of the union.
41 * On update and upsert, the broadest member is the atomic operation, which is not ideal in many cases.
42 *
43 * @default true
44 */
45 atomicOperations?: boolean
46}

Getting started

There are two ways you can start with the Prisma plugin. Either from scratch, or using an existing database.

From scratch

  1. Create a schema.prisma file

    1//schema.prisma
    2
    3generator prisma_client {
    4 provider = "prisma-client-js"
    5}
    6
    7model User {
    8 id Int @id @default(autoincrement())
    9 name String
    10}
  2. Add a datasource to your schema. We recommend you use Postgres but MySQL and SQLite are also supported.

    To add your datasource, simply copy/paste one of the block below at the top of your schema.prisma file

    Note: You can also pass the database credentials via a .env file. Read more about it here

    Using PostgreSQL:

    1//schema.prisma
    2
    3datasource db {
    4 provider = "postgres"
    5 url = "postgresql://USER:PASSWORD@localhost:5432/DATABASE"
    6}

    Using MySQL:

    1//schema.prisma
    2
    3datasource db {
    4 provider = "mysql"
    5 url = "mysql://USER:PASSWORD@localhost:3306/DATABASE"
    6}

    Using SQLite:

    1//schema.prisma
    2
    3datasource db {
    4 provider = "sqlite"
    5 url = "file:./dev.db"
    6}
  3. Create a migration from changes in Prisma schema and run the migration

    $npx prisma migrate dev

You're ready to start working!

From an existing database

When starting from an existing database, you should use Prisma's introspection feature.

  1. Create a schema.prisma file.

Create a schema.prisma file and add your database credentials in it so that Prisma can introspect your database schema.

Note: You can also pass the database credentials via a .env file. Read more about it here

1Using PostgreSQL:
2
3```prisma
4//schema.prisma
5
6datasource db {
7 provider = "postgres"
8 url = "postgresql://USER:PASSWORD@localhost:5432/DATABASE"
9}
10```
11
12Using MySQL:
13
14```prisma
15//schema.prisma
16
17datasource db {
18 provider = "mysql"
19 url = "mysql://USER:PASSWORD@localhost:3306/DATABASE"
20}
21```
22
23Using SQLite:
24
25```prisma
26//schema.prisma
27
28datasource db {
29 provider = "sqlite"
30 url = "file:./dev.db"
31}
32```
  1. Introspect your database:
$npx prisma db pull
  1. Generate the Prisma Client. Add the following block at the top of your schema.prisma file:
1generator prisma_client {
2 provider = "prisma-client-js"
3}

The plugin will take care of generating the Prisma Client for you after that.

You're ready to start working!

Example

Given a Prisma schema (left), you will be able to project these Prisma models onto your API and expose operations against them (middle) resulting in the GraphQL Schema (right).

Note: t.crud is an experimental feature. You must explicitly enable it via the plugin options.

1generator prisma_client {
2 provider = "prisma-client-js"
3}
4
5model User {
6 id String @id @default(cuid())
7 email String @unique
8 birthDate DateTime
9}
10
11model Post {
12 id String @id @default(cuid())
13 author User[]
14}
15
1import { schema } from 'nexus'
2
3schema.queryType({
4 definition(t) {
5 t.crud.user()
6 t.crud.users({
7 ordering: true,
8 })
9 t.crud.post()
10 t.crud.posts({
11 filtering: true,
12 })
13 },
14})
15
16schema.mutationType({
17 definition(t) {
18 t.crud.createOneUser()
19 t.crud.createOnePost()
20 t.crud.deleteOneUser()
21 t.crud.deleteOnePost()
22 },
23})
24
25schema.objectType({
26 name: 'User',
27 definition(t) {
28 t.model.id()
29 t.model.email()
30 t.model.birthDate()
31 t.model.posts()
32 },
33})
34
35schema.objectType({
36 name: 'Post',
37 definition(t) {
38 t.model.id()
39 t.model.author()
40 },
41})
1scalar DateTime
2
3input DateTimeFilter {
4 equals: DateTime
5 gt: DateTime
6 gte: DateTime
7 in: [DateTime!]
8 lt: DateTime
9 lte: DateTime
10 not: DateTime
11 notIn: [DateTime!]
12}
13
14type Mutation {
15 createOnePost(data: PostCreateInput!): Post!
16 createOneUser(data: UserCreateInput!): User!
17 deleteOnePost(where: PostWhereUniqueInput!): Post
18 deleteOneUser(where: UserWhereUniqueInput!): User
19}
20
21enum OrderByArg {
22 asc
23 desc
24}
25
26type Post {
27 author(after: String, before: String, first: Int, last: Int): [User!]!
28 id: ID!
29}
30
31input PostCreateInput {
32 author: UserCreateManyWithoutAuthorInput
33 id: ID
34}
35
36input PostCreateManyWithoutPostsInput {
37 connect: [PostWhereUniqueInput!]
38 create: [PostCreateWithoutAuthorInput!]
39}
40
41input PostCreateWithoutAuthorInput {
42 id: ID
43}
44
45input PostFilter {
46 every: PostWhereInput
47 none: PostWhereInput
48 some: PostWhereInput
49}
50
51input PostWhereInput {
52 AND: [PostWhereInput!]
53 author: UserFilter
54 id: StringFilter
55 NOT: [PostWhereInput!]
56 OR: [PostWhereInput!]
57}
58
59input PostWhereUniqueInput {
60 id: ID
61}
62
63type Query {
64 post(where: PostWhereUniqueInput!): Post
65 posts(after: String, before: String, first: Int, last: Int, where: PostWhereInput): [Post!]!
66 user(where: UserWhereUniqueInput!): User
67 users(after: String, before: String, first: Int, last: Int, orderBy: UserOrderByInput): [User!]!
68}
69
70input StringFilter {
71 contains: String
72 endsWith: String
73 equals: String
74 gt: String
75 gte: String
76 in: [String!]
77 lt: String
78 lte: String
79 not: String
80 notIn: [String!]
81 startsWith: String
82}
83
84type User {
85 birthDate: DateTime!
86 email: String!
87 id: ID!
88 posts(after: String, before: String, first: Int, last: Int): [Post!]!
89}
90
91input UserCreateInput {
92 birthDate: DateTime!
93 email: String!
94 id: ID
95 posts: PostCreateManyWithoutPostsInput
96}
97
98input UserCreateManyWithoutAuthorInput {
99 connect: [UserWhereUniqueInput!]
100 create: [UserCreateWithoutPostsInput!]
101}
102
103input UserCreateWithoutPostsInput {
104 birthDate: DateTime!
105 email: String!
106 id: ID
107}
108
109input UserFilter {
110 every: UserWhereInput
111 none: UserWhereInput
112 some: UserWhereInput
113}
114
115input UserOrderByInput {
116 birthDate: OrderByArg
117 email: OrderByArg
118 id: OrderByArg
119}
120
121input UserWhereInput {
122 AND: [UserWhereInput!]
123 birthDate: DateTimeFilter
124 email: StringFilter
125 id: StringFilter
126 NOT: [UserWhereInput!]
127 OR: [UserWhereInput!]
128 posts: PostFilter
129}
130
131input UserWhereUniqueInput {
132 email: String
133 id: ID
134}

Recipes

Projecting Prisma model fields

Exposing one of your Prisma models in your GraphQL API

1schema.objectType({
2 name: 'Post',
3 definition(t) {
4 t.model.id()
5 t.model.title()
6 t.model.content()
7 },
8})

Simple computed GraphQL fields

You can add computed fields to a GraphQL object using the standard GraphQL Nexus API.

1schema.objectType({
2 name: "Post",
3 definition(t) {
4 t.model.id()
5 t.model.title()
6 t.model.content()
7 t.string("uppercaseTitle", {
8 resolve({ title }, args, ctx) {
9 return title.toUpperCase(),
10 }
11 })
12 },
13})

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 prisma instance that's attached to the context and implement your resolver based on that.

1schema.objectType({
2 name: 'Post',
3 definition(t) {
4 t.model.id()
5 t.model.content()
6 t.string('anotherComputedField', {
7 async resolve(_parent, _args, ctx) {
8 const databaseInfo = await ctx.prisma.someModel.someOperation(...)
9 const result = doSomething(databaseInfo)
10 return result
11 }
12 })
13 }
14})

Project a Prisma field to a differently named GraphQL field

1schema.objectType({
2 name: 'Post',
3 definition(t) {
4 t.model.id()
5 t.model.content({
6 alias: 'body',
7 })
8 },
9})

t.crud is an experimental feature. You must explicitly enable it via the plugin options.

1schema.queryType({
2 definition(t) {
3 t.crud.post()
4 t.crud.posts({
5 ordering: true,
6 filtering: true,
7 })
8 },
9})

Publish writes on a Prisma model

t.crud is an experimental feature. You must explicitly enable it via the plugin options.

1schema.mutationType({
2 definition(t) {
3 t.crud.createPost()
4 t.crud.updatePost()
5 t.crud.updateManyPost()
6 t.crud.upsertPost()
7 t.crud.deletePost()
8 t.crud.deleteManyPost()
9 },
10})

Publish customized reads on a Prisma model

t.crud is an experimental feature. You must explicitly enable it via the plugin options.

1schema.queryType({
2 definition(t) {
3 t.crud.posts({
4 filtering: {
5 id: true,
6 title: true,
7 },
8 ordering: { title: true },
9 })
10 },
11})

Publish autogenerated mutations with computed input values

t.crud is an experimental feature. You must explicitly enable it via the plugin options.

1schema.mutationType({
2 definition(t) {
3 /*
4 Assuming our prisma model for User has a createdByBrowser field,
5 this removes it from the input type but ensures the value is
6 inferred from context and passed to Prisma Client.
7 */
8 t.crud.createOneUser({
9 computedInputs: {
10 createdByBrowser: ({ args, ctx, info }) => ctx.session.browser,
11 },
12 })
13 },
14})

Globally remove a field from input types and infer its value

🚧 Work in progress.

1nexusPrismaPlugin({
2 ...other config...
3 /*
4 Remove fields named "user" from all input types. When resolving
5 a request whose data contains any of these types, the value is inferred
6 from context and passed to Prisma Client, even if it's nested. This is great for
7 creating data associated with one user's account.
8 */
9 computedInputs: {
10 user: ({ args, ctx, info }) => ({
11 connect: {
12 id: ctx.userId,
13 },
14 }),
15 },
16})
1schema.mutationType({
2 definition(t) {
3 t.crud.createOnePost()
4 },
5})

Without computedInputs:

1mutation createOnePost {
2 createOnePost(
3 data: {
4 title: "Automatically generate clean APIs!"
5 image: { url: "https://example.com/images/prancing-unicorns", user: { connect: { id: 1 } } }
6 user: { connect: { id: 1 } }
7 }
8 )
9}

With computedInputs:

1mutation createOnePost {
2 createOnePost(
3 data: {
4 title: "Automatically generate clean APIs!"
5 image: { url: "https://example.com/images/prancing-unicorns" }
6 }
7 )
8}

Publish model writes along side Prisma Client-resolved fields

t.crud is an experimental feature. You must explicitly enable it via the plugin options.

1schema.mutationType({
2 definition(t) {
3 t.crud.createUser()
4 t.crud.updateUser()
5 t.crud.deleteUser()
6 t.crud.deletePost()
7
8 t.field('createDraft', {
9 type: 'Post',
10 args: {
11 title: stringArg(),
12 content: nullable(stringArg()),
13 },
14 resolve: (parent, { title, content }, ctx) => {
15 return ctx.prisma.posts.createPost({ title, content })
16 },
17 })
18
19 t.field('publish', {
20 type: nullable('Post'),
21 args: {
22 id: idArg(),
23 },
24 resolve(parent, { id }, ctx) {
25 return ctx.prisma.posts.updatePost({
26 where: { id },
27 data: {
28 published: true,
29 },
30 })
31 },
32 })
33 },
34})
Edit this page on Github