Tutorials
Setup Prisma
Demo Server
Create New DB
MySQL
Postgres
Connect Empty DB
MySQL
Postgres
Connect DB with Data
MySQL
Postgres
Build GraphQL Servers
Development
Build a GraphQL Server with Prisma
Build a GraphQL Server from Scratch
Build a Realtime GraphQL Server with Subscriptions
Deployment
Deployment with Now
Deployment with Up
Access Prisma from Scripts
Access Prisma from a Node script using Prisma Bindings
Connect to Prisma from the frontend
Bootstrapping Boilerplates
Reference
Service Configuration
Overview
Data Model
Data Modelling (SDL)
Migrations
Introspection
Server-side Subscriptions
Prisma Servers & DBs
Prisma Servers
OverviewDocker
Database Connectors
OverviewMySQLPostgres
Prisma Bindings
OverviewAPI
Code generation
Upgrade Guides
Upgrading Prisma
Overview
Upgrade to 1.7
Upgrade to 1.8

Concepts

Last updated a day ago Edit this page

#Data model and Prisma database schema

The Prisma API of a Prisma service is fully centered around its data model.

The API is automatically generated based on the data model that's associated with your Prisma service.

Every operation exposed in the Prisma API is associated with a model or relation from your data model:

  • Queries

    • query one or more nodes of a certain model
    • query nodes across relations
    • query data aggregated across relations
  • Mutations

    • create, update, upsert and delete nodes of a certain model
    • create, connect, disconnect, update and upsert nodes across relations
    • batch update or delete nodes of a certain model
  • Subscriptions

    • get notified about created, updated and deleted nodes

The actual GraphQL schema defining the available GraphQL operations in the Prisma API is also referred to as Prisma database schema.

You can learn more about the differences between the data model and the prisma database in the data modelling chapter.

#Advanced API concepts

#Node selection

Many operations in the Prisma API only affect a subset of the existing nodes in the database, oftentimes even only a single node.

In these case, you need a way to ask for specific nodes in the API - most of the time this is done via a where argument.

Nodes can be selected via any field that's annotated with the @unique directive.

For the following examples, consider the following simple data model:

1
2
3
4
5
type Post {
  id: ID! @unique
  title: String!
  published: Boolean @default(value: "false")
}

Here are a few scenarios where node selection is required.

Retrieve a single node by its email:

1
2
3
4
5
6
7
query {
  post(where: {
    email: "hello@graph.cool"
  }) {
    id
  }
}

Update the title of a single node:

1
2
3
4
5
6
7
8
9
10
11
12
mutation {
  updatePost(
    where: {
      id: "ohco0iewee6eizidohwigheif"
    }
    data: {
      title: "GraphQL is awesome"
    }
  ) {
    id
  }
}

Update published of a many nodes at once (also see Batch operations):

1
2
3
4
5
6
7
8
9
10
11
12
mutation {
  updatePost(
    where: {
      id_in: ["ohco0iewee6eizidohwigheif", "phah4ooqueengij0kan4sahlo", "chae8keizohmiothuewuvahpa"]
    }
    data: {
      published: true
    }
  ) {
    count
  }
}

#Batch operations

One application of the node selection concept is the exposed batch operations. Batch updating or deleting is optimized for making changes to a large number of nodes. As such, these mutations only return how many nodes have been affected, rather than full information on specific nodes.

For example, the mutations updateManyPosts and deleteManyPosts provide a where argument to select specific nodes, and return a count field with the number of affected nodes (see the example above).

Note that no subscription events are triggered for batch mutations!

#Connections

In contrast to the simpler object queries that directly return a list of nodes, connection queries are based on the Relay Connection model. In addition to pagination information, connections also offer advanced features like aggregation.

For example, while the posts query allows you to select specific Post nodes, sort them by some field and paginate over the result, the postsConnection query additionally allows you to count all unpublished posts:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
query {
  postsConnection {
    # `aggregate` allows to perform common aggregation operations
    aggregate {
      count
    }
    edges {
      # each `node` refers to a single `Post` element
      node {
        title
      }
    }
  }
}

#Transactional mutations

Single mutations in the Prisma API that are not batch operations are always executed transactionally, even if they consist of many actions that potentially spread across relations. This is especially useful for nested mutations that perform several database writes on multiple types.

An example is creating a User node and two Post nodes that will be connected, while also connecting the User node to two other, already existing Post nodes, all in a single mutation. If any of the mentioned actions fail (for example because of a violated @unique field constraint), the entire mutation is rolled back!

Mutations are transactional, meaning they are atomic and isolated. This means that between two separate actions of the same nested mutation, no other mutations can alter the data. Also the result of a single action cannot be observed until the complete mutation has been processed.

#Cascading deletes

Prisma supports different deletion behaviours for relations in your data model. There are two major deletion behaviours:

  • CASCADE: When a node with a relation to one or more other nodes gets deleted, these nodes will be deleted as well.
  • SET_NULL: When a node with a relation to one or more other nodes gets deleted, the fields referring to the deleted node are set to null.

Consider this example:

As mentioned above, you can specify a dedicated deletion behaviour for the related nodes. That's what the onDelete argument of the @relation directive is for.

Consider the following example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
type User {
  id: ID! @unique
  comments: [Comment!]! @relation(name: "CommentAuthor", onDelete: CASCADE)
  blog: Blog @relation(name: "BlogOwner", onDelete: CASCADE)
}

type Blog {
  id: ID! @unique
  comments: [Comment!]! @relation(name: "Comments", onDelete: CASCADE)
  owner: User! @relation(name: "BlogOwner", onDelete: SET_NULL)
}

type Comment {
  id: ID! @unique
  blog: Blog! @relation(name: "Comments", onDelete: SET_NULL)
  author: User @relation(name: "CommentAuthor", onDelete: SET_NULL)
}

Let's investigate the deletion behaviour for the three types:

  • When a User node gets deleted,

    • all related Comment nodes will be deleted.
    • the related Blog node will be deleted.
  • When a Blog node gets deleted,

    • all related Comment nodes will be deleted.
    • the related User node will have its blog field set to null.
  • When a Comment node gets deleted,

    • the related Blog node continues to exist and the deleted Comment node is removed from its comments list.
    • the related User node continues to exist and the deleted Comment node is removed from its comments list.

You can find more detailled info about the @relation directive and its usage here.

#Authentication

#API secret

The GraphQL API of a Prisma service is typically protected by an API secret which you specify as the secret property in in prisma.yml.

Here is an example of a prisma.yml specifying a secret:

1
2
3
4
endpoint: http://localhost:4466/myapi/dev
datamodel: datamodel.graphql

secret: mysecret123 # your API secret

#API token

API tokens are used to authenticate against your Prisma API. The API secret is used to sign a JWT which needs be used in the Authorization header of the HTTP requests made against your Prisma API:

Authorization: Bearer __YOUR_API_TOKEN__

Obtaining an API token with the Prisma CLI

The easiest way to obtain an API token is by using the prisma token command from the Prisma CLI:

prisma token

When running prisma token inside a directory where prisma.yml is available, the CLI will read the secret property from prisma.yml and generate a corresponding JWT.

Authenticating in a GraphQL Playground

After obtainining an API token for your Prisma API, you can use it to authenticate your API requests. For example, when using the API through a GraphQL Playground.

Once you openened the Playground for your Prisma API, open the HTTP HEADERS field in the bottom-left corner of the Playground. Then paste your API token as the value for the Authorization field into it:

1
2
3
{
  "Authorization": "Bearer __YOUR_API_TOKEN__"
}

With a real token, this might look like this:

1
2
3
{
  "Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7InNlcnZpY2UiOiJibG9nckBkZXYiLCJyb2xlcyI6WyJhZG1pbiJdfSwiaWF0IjoxNTE4NzE2NjA4LCJleHAiOjE1MTkzMjE0MDh9.zqBh_Oo4RmV4j3UQeVDYqJDxV-YHQiOR-XIlhjbWejw"
}

Claims

The JWT must contain different claims:

  • Expiration time: exp, the expiration time of the token.
  • Service information: service, the name and stage of the service

In the future there might be support for more fine grained access control by introducing a concept of roles such as ["write:Log", "read:*"]

Here is the sample Payload of a JWT.

1
2
3
4
{
  "exp": 1300819380,
  "service": "my-service@prod"
}

Generating a service token in JavaScript

Consider the following prisma.yml:

1
2
3
4
5
6
7
8
service: my-service

stage: ${env:PRISMA_STAGE}
cluster: ${env:PRISMA_CLUSTER}

datamodel: database/datamodel.graphql

secret: ${env:PRISMA_SECRET}

Note that this example uses environment variables inside prisma.yml.

A Node server could create a signed JWT, based on the jsonwebtoken library, for the stage PRISMA_STAGE of the service my-service like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
var jwt = require('jsonwebtoken')

jwt.sign(
  {
    data: {
      service: 'my-service@' + process.env.PRISMA_STAGE,
    },
  },
  process.env.PRISMA_SECRET,
  {
    expiresIn: '1h',
  }
)

JWT verification

For requests made against a Prisma service, the following properties of the JWT will be verified:

  • It must be signed with a secret configured for the service
  • It must contain an exp claim with a time value in the future
  • It must contain a service claim with service and stage matching the current request

#Error handling

When an error occurs for one of your queries or mutations, the response contains an errors property with more information about the error code, the error message and more.

There are two kind of API errors:

  • Application errors usually indicate that your request was invalid.
  • Internal server errors usually mean that something unexpected happened inside of the Prisma service. Check your service logs for more information.

Note: The errors field behaves according to the official GraphQL specification for error handling.

#Application errors

An error returned by the API usually indicates that something is not correct with the requested query or mutation. You might have accidentally made a typo or forgot a required argument in your query. Try to investigate your input for possible errors related to the error message.

Troubleshooting

Here is a list of common errors that you might encounter:

Authentication
Insufficient permissions / Invalid token
1
2
3
4
5
6
7
8
9
10
{
  "errors": [
    {
      "code": 3015,
      "requestId": "api:api:cjc3kda1l000h0179mvzirggl",
      "message":
        "Your token is invalid. It might have expired or you might be using a token from a different project."
    }
  ]
}

Check if the token you provided has not yet expired and is signed with a secret listed in prisma.yml.

#Internal server errors

Consult the service logs for more information on the error. For the local cluster, this can be done using the prisma logs command.

Was this page helpful?