Getting started / Tutorial

5. Persisting data (via Prisma)

Overview

So far we have been working with in-memory data while we learn about other parts of Nexus in a focused manner, but in this chapter we're going to put the focus squarely on data and show how Nexus can be used with a database. This marks an important step toward your blog app becoming more real.

In this section, you'll learn how to add Prisma to your GraphQL API. Prisma is a new way of working with databases that we'll learn more about in a moment.

Its important to understand that Nexus does not require these technology choices and could actually be used with any database and abstractions over them (raw SQL, query builder, ORM..). However, Nexus is built by a team at Prisma (the company) and unsurprisingly there is great integration between its tools and Nexus.

What is Prisma?

So, what is Prisma? It is an open source database toolkit that consists of the following parts:

  • Prisma Client: Auto-generated and type-safe query builder for Node.js & TypeScript
  • Prisma Migrate (Preview): Declarative data modeling & migration system
  • Prisma Studio: GUI to view and edit data in your database

At the heart of Prisma is the Prisma Schema, a file usually called schema.prisma, that you will see later in this tutorial. It is a declarative file wherein using a domain specific language you encode your database schema, connection to the database, and more. Prisma currently supports 4 relational databases: MySQL, PostgreSQL, SQL Server and SQLite.

Prisma has great docs so definitely check them out at some point. For now you can stay in the flow of this tutorial if you want though. We're going to focus on Prisma Client.

Set up Prisma

Now that you know a bit about Prisma, let's get going! Do the following:

  • Install the Prisma client and CLI
  • Use it in your api/schema.ts module
  • Create your Prisma Schema

like so:

$npm install @prisma/client
$npm install --save-dev prisma

Next, add Prisma to your project by creating your Prisma schema file with the following command:

$npx prisma init

The command creates a new directory called prisma which will contain a schema.prisma file and add a .env file at the root of your project.

For ease of set up, this guide will use SQLite. If you wish to use your preferred database, you can make the switch by simply changing the provider and url in your schema.prisma file.

To connect to your database, you'll need to set the url field in the datasource block in your Prisma schema. Since the url is set via an environment variable, you will define it in .env.

1DATABASE_URL="postgresql://postgres:postgres@localhost:5432/myapp"

Create your first database model

It is now time to replace our in-memory data with actual tables in our database. To do this we'll write models in our Prisma Schema.

In chapters 2 and 3 we already began to model our blog domain with the GraphQL type Post. We can base our models on that prior work, resulting in a Prisma Schema like so:

Diff
Code
1// prisma/schema.prisma
2
3datasource db {
+ provider = "sqlite"
+ url = "file:./dev.db"
6}
7
8generator client {
9 provider = "prisma-client-js"
10}
11
+model Post {
+ id Int @id @default(autoincrement())
+ title String
+ body String
+ published Boolean
+}

With our database schema specified, we're now ready to proceed to our first database migration! To do that, we'll use the Prisma CLI.

$npx prisma migrate dev --name init --preview-feature

This will create a database migration called init. Once a migration is complete, the Prisma CLI will create dev.db database and apply the changes against your database. Once the first migration is complete, the Prisma CLI will install @prisma/client package. In subsequent migrations, Prisma CLI will generate the Prisma Client.

Access your database

Now let's finally ditch our in-memory data! Let's replace it with the Prisma Client

Diff
Code
1// api/db.ts
+import { PrismaClient } from '@prisma/client'
3
4export const db = new PrismaClient()
5
-export interface Post {
- id: number
- title: string
- body: string
- published: boolean
-}
12
-export interface Db {
- posts: Post[]
-}
16
-export const db: Db = {
- posts: [{ id: 1, title: 'Nexus', body: '...', published: false }],
-}

Then, update your Context type so that it uses the PrismaClient created in ./db.ts

Diff
Code
1// api/context.ts
2import { db } from "./db";
+import { PrismaClient } from "@prisma/client"
4
5export interface Context {
- db: Db
+ db: PrismaClient
8}
9
10export const context = {
11 db,
12}

Let's now replace all our previous in-memory db interactions with calls to the Prisma Client

Diff
Code
1// api/graphql/Post.ts
2// ...
3
4export const PostQuery = extendType({
5 type: 'Query',
6 definition(t) {
7 t.list.field('drafts', {
8 type: 'Post',
9 resolve(_root, _args, ctx) {
- return ctx.db.posts.filter((p) => p.published === false)
+ return ctx.db.post.findMany({ where: { published: false } })
12 },
13 });
14 t.list.field('posts', {
15 type: 'Post',
16 resolve(_root, _args, ctx) {
- return ctx.db.posts.filter((p) => p.published === true)
+ return ctx.db.post.findMany({ where: { published: true } })
19 },
20 })
21 },
22})
23
24export const PostMutation = extendType({
25 type: 'Mutation',
26 definition(t) {
27 t.field('createDraft', {
28 type: 'Post',
29 args: {
30 title: nonNull(stringArg()),
31 body: nonNull(stringArg()),
32 },
33 resolve(_root, args, ctx) {
34 const draft = {
- id: ctx.db.posts.length + 1,
36 title: args.title,
37 body: args.body,
38 published: false,
39 }
- ctx.db.posts.push(draft)
41
- return draft
+ return ctx.db.post.create({ data: draft })
44 },
45 })
46
47 t.field('publish', {
48 type: 'Post',
49 args: {
50 draftId: nonNull(intArg()),
51 },
52 resolve(_root, args, ctx) {
- let postToPublish = ctx.db.posts.find((p) => p.id === args.draftId)
54
- if (!postToPublish) {
- throw new Error('Could not find draft with id ' + args.draftId)
- }
58
- postToPublish.published = true
60
- return postToPublish
62
+ return ctx.db.post.update({
+ where: { id: args.draftId },
+ data: {
+ published: true,
+ },
+ });
69 },
70 })
71 },
72})

Try it out

Awesome, you're ready to open up the playground and create a draft! If all goes well, good job! If not, no worries, there's a lot of integration pieces in this chapter where something could have gone wrong. If after reviewing your steps you still don't understand the issue, feel free to open up a discussion asking for help.

Wrapping up

We've just changed our code, so we must be due or overdue for a test update right? Well, in the next chapter we'll do just that, and show you how Nexus testing works with Prisma.

Next →
Edit this page on Github