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:
$$
Next, add Prisma to your project by creating your Prisma schema file with the following command:
$
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:
1// prisma/schema.prisma23datasource db {+ provider = "sqlite"+ url = "file:./dev.db"6}78generator 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.
$
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
1// api/db.ts+import { PrismaClient } from '@prisma/client'3+export 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
1// api/context.ts2import { db } from "./db";+import { PrismaClient } from "@prisma/client"45export interface Context {- db: Db+ db: PrismaClient8}910export const context = {11 db,12}
Let's now replace all our previous in-memory db interactions with calls to the Prisma Client
1// api/graphql/Post.ts2// ...34export 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})2324export 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 })4647 t.field('publish', {48 type: 'Post',49 args: {50 draftId: nonNull(intArg()),51 },52 resolve(_root, args, ctx) {- let draftToPublish = ctx.db.posts.find((p) => p.id === args.draftId)54- if (!draftToPublish) {- throw new Error('Could not find draft with id ' + args.draftId)- }58- draftToPublish.published = true60- return draftToPublish62+ 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 →