Prisma GraphQL APIReference

Subscriptions

Overview

How are subscriptions in the Prisma API generated?

The GraphQL API of a Prisma service is specified in the Prisma GraphQL schema. The Prisma GraphQL schema is auto-generated based on the service's datamodel:

The Subscription type of the Prisma GraphQL schema defines all the subscriptions the Prisma API accepts.

As an example, consider the following datamodel:

type User {
  id: ID! @unique
  name: String!
}

This is the Subscription type Prisma will generate:

type Subscription {
  user(where: UserSubscriptionWhereInput): UserSubscriptionPayload
}

For every type in your datamodel, one subscription is generated. Taking above the above User type as an example, this subscription is:

  • user: Fires whenever a User node is created, updated or deleted. (Except for batch mutations).

To inspect all the available operations of your Prisma API in detail, you can read the Prisma GraphQL schema of your Prisma service. It can be downloaded with the GraphQL CLI:

graphql get-schema --endpoint __YOUR_PRISMA_ENDPOINT__ --output prisma.graphql --no-all

Another way to learn about the concrete capabilities of your Prisma API is by exploring the auto-generated API documentation inside a GraphQL Playground. You can do so by clicking the green SCHEMA-button at the right edge of the Playground:

Datamodel for examples on this page

All example subscriptions on this page are based on a Prisma service configured with this datamodel:

type Post {
  id: ID! @unique
  title: String!
  published: Boolean! @default(value: "false")
  author: User
}

type User {
  id: ID! @unique
  name: String!
  posts: [Post!]!
}

Understanding Prisma’s subscription API

Prisma lets you subscribe to three different kinds of events (per type in your datamodel). Taking the Post type from above datamodel as an example, these events are:

  • a new Post nodes is created
  • an existing Post nodes is updated
  • an existing Post nodes is deleted

Subscriptions are not firing for batch mutations.

The corresponding definition of the Subscription type looks as follows (this definition can be found in the Prisma GraphQL schema):

type Subscription {
  post(where: PostSubscriptionWhereInput): PostSubscriptionPayload
}

Here is what a sdle subscription operation looks like:

subscription {
  post {
    node {
      id
      title
    }
  }
}

If not further constrained through the where argument, the post subscription will fire for all of the events mentioned above. Which fields of the PostSubscriptionPayload are included in messages from the server depends on the kind of event.

Filtering for specific events

The where argument allows clients to specify exactly what events they’re interested in. Maybe a client always only wants to receive updates when...

  • ... a Post gets deleted
  • ... a Post where the title contains a specific keyword is created

These kinds of constraints can be expressed using the where argument. The type of where is PostSubscriptionWhereInput:

input PostSubscriptionWhereInput {
  # Filter for a specific mutation:
  # CREATED, UPDATED, DELETED
  mutation_in: [MutationType!]

  # Filter for a specific field being updated
  updatedFields_contains: String
  updatedFields_contains_every: [String!]
  updatedFields_contains_some: [String!]

  # Filter for concrete values of the Post being mutated
  node: PostWhereInput

  # Combine several filter conditions
  AND: [PostSubscriptionWhereInput!]
  OR: [PostSubscriptionWhereInput!]
}

The two examples mentioned above could be expressed with the following subscriptions in the Prisma API:

# Only fire for _deleted_ posts
subscription {
  post(where: {
    mutation_in: [DELETED]
  }) {
    # ... we'll talk about the selection set in a bit
  }
}

# Only fire when a post whose title contains "GraphQL" is _created_
subscription {
  post(where: {
    mutation_in: [CREATED]
    node: {
      title_contains: "GraphQL"
    }
  }) {
    # ... we'll talk about the selection set in a bit
  }
}

Exploring the selection set of a subscription

You now have a good understanding how you can subscribe to the events that interest you. But how can you now ask for the data related to an event?

The PostSubscriptionPayload type defines the fields which you can request in a post subscription. Here is what it looks like:

type PostSubscriptionPayload {
  mutation: MutationType!
  node: Post
  updatedFields: [String!]
  previousValues: PostPreviousValues
}

Here's an overview of the values for each field according to the event that triggered the subscription:

mutationnodepreviousValuesupdatedFields
CREATEThe created nodenullnull
UPDATEThe new values of the updated nodeThe values of the updated node before the updateA list of strings with the names of the fields that were updated
DELETEnullThe deleted nodenull

In the following, each field is discussed in more detail.

mutation

The mutation field has the type MutationType. MutationType is an enum with three values:

enum MutationType {
  CREATED
  UPDATED
  DELETED
}

The mutation field on the PostSubscriptionPayload type therefore carries the information what kind of mutation happened.

node

The node field has the Post type. It represents the Post element which was created or updated and lets you retrieve further information about it.

Notice that for DELETED-mutations, node will always be null. If you need to know more details about deleted Post node, you can use the previousValues field instead (more about that soon).

updatedFields

updatedFields is of type [String!].

One piece of information you might be interested in for UPDATED-mutations is which fields have been updated with a mutation. That’s what the updatedFields field is used for.

Assume a client has subscribed to the Prisma API with the following subscription:

subscription {
  post {
    updatedFields
  }
}

Now, assume the server receives the following mutation to update the title of a given Post:

mutation {
  updatePost(
    where: { id: "..." }
    data: { title: "Prisma is the best way to build GraphQL servers" }
  ) {
    id
  }
}

The subscribed client will then receive the following payload:

{
  "data": {
    "post": {
      "updatedFields": ["title"]
    }
  }
}

This is because the mutation only updated the Post node's title field - nothing else.

previousValues

previousValues is of type PostPreviousValues. This type looks very similar to Post itself:

type PostPreviousValues {
  id: ID!
  title: String!
}

It basically is a helper type mirroring the fields from Post.

previousValues is only used for UPDATED- and DELETED-mutations. For CREATED-mutations, it will always be null (for the same reason that node is null for DELETED-mutations).

Putting everything together

Consider again the sample updatePost mutation from before. But now assume, the subscription query includes all the fields we just discussed:

subscription {
  post {
    mutation
    updatedFields
    node {
      title
    }
    previousValues {
      title
    }
  }
}

Here’s what the payload will look like that the server pushes to the client after it performed the mutation from before:

{
  "data": {
    "post": {
      "mutation": "UPDATED",
      "updatedFields": ["title"],
      "node": {
        "title": "Prisma is the best way to build GraphQL servers"
      },
      "previousValues": {
        "title": "GraphQL servers are best built with conventional ORMs"
      }
    }
  }
}

Note that this assumes the updated Post had the following title before the mutation was performed: “GraphQL servers are best built with conventional ORMs”.

Object subscriptions

For every available object type in your datamodel, one object subscription is automatically generated.

Subscribing to created nodes

For a given type, you can subscribe to all nodes that are being created using the generated object subscription.

Subscribe to all created nodes

To subscribe to created nodes of a certain type, you can use the generated object subscription and specify the mutation_in: [CREATED] filter for the where argument.

Subscribe to created Post nodes:

subscription {
  post(where: { mutation_in: [CREATED] }) {
    mutation
    node {
      description
      imageUrl
      author {
        id
      }
    }
  }
}

Subscribe to specific created nodes using filters

You can make use of a similar filter system as for queries using the node argument of the where object.

Subscribe to created Post nodes of a specific author:

subscription {
  post(where: {
    AND: [
      {
        mutation_in: [CREATED]
      }, {
      node: {
        author: {
          id: "cj03x3nacox6m0119755kmcm3"
        }
      }
    ]
  }) {
    mutation
    node {
      description
      imageUrl
      author {
        id
      }
    }
  }
}

Subscribing to deleted nodes

To subscribe to deleted nodes of a certain type, you can use the generated object subscription and specify the mutation_in: [DELETED] filter for the where argument.

Subscribe to all deleted nodes

Subscribe to deleted Post nodes:

subscription deletePost {
  post(where: { mutation_in: [DELETED] }) {
    mutation
    previousValues {
      id
    }
  }
}

Subscribe to specific deleted nodes

You can make use of a similar filter system as for queries using the node argument of the where object.

Subscribe to deleted Post nodes where the title contains a certain string:

subscription {
  post(where: { mutation_in: [DELETED], node: { title_contains: "GraphQL" } }) {
    mutation
    previousValues {
      id
    }
  }
}

Subscribing to updated nodes

To subscribe to updated nodes of a certain type, you can use the generated object subscription and specify the mutation_in: [UPDATED] filter for the where argument.

Subscribe to all updated nodes

Subscribe to updated Post nodes:

subscription {
  post(where: { mutation_in: [UPDATED] }) {
    mutation
    updatedFields
    node {
      description
      imageUrl
    }
    previousValues {
      description
      imageUrl
    }
  }
}

Subscribe to specific field updates

Subscribe to events where the description field of a Post node gets updated:

subscription {
  post(
    where: { mutation_in: [UPDATED], updatedFields_contains: "description" }
  ) {
    mutation
    node {
      description
    }
    updatedFields
    previousValues {
      description
    }
  }
}

Similarly to updatedFields_contains, more filter conditions exist:

  • updatedFields_contains_every: [String!]: Matches if all fields specified have been updated.
  • updatedFields_contains_some: [String!]: Matches if some of the specified fields have been updated.

Subscribe to events where the description and published fields of a Post node gets updated:

subscription {
  post(
    where: {
      mutation_in: [UPDATED]
      updatedFields_contains_every: ["description", "published"]
    }
  ) {
    mutation
    node {
      description
    }
    updatedFields
    previousValues {
      description
    }
  }
}

Subscribe to events where the description or published fields of a Post node gets updated:

subscription {
  post(
    where: {
      mutation_in: [UPDATED]
      updatedFields_contains_some: ["description", "published"]
    }
  ) {
    mutation
    node {
      description
    }
    updatedFields
    previousValues {
      description
    }
  }
}

It is not possible to use the updatedFields filter together with mutation_in: [CREATED] or mutation_in: [DELETED] as it only applied to UPDATE events!

Relation subscriptions

Currently, subscriptions for relation updates are only available with a workaround using UPDATED subscriptions.

You can force a notification by "touching" nodes. Add a dummy: String field to the type in question and update this field for the node whose relation status just changed.

mutation updatePost {
  updatePost(
    where: { id: "some-id" }
    data: {
      dummy: "dummy" # do a dummy change to trigger update subscription
    }
  )
}

If you're interested in a direct relation trigger for subscriptions, please join the discussion on GitHub.