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 aUser
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 thetitle
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:
mutation | node | previousValues | updatedFields |
---|---|---|---|
CREATE | The created node | null | null |
UPDATE | The new values of the updated node | The values of the updated node before the update | A list of strings with the names of the fields that were updated |
DELETE | null | The deleted node | null |
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.