Learn something new with GraphQL every week

Project Setup

#6 Install and configure Prisma

Install and configure Prisma
Julian Mayorga
Instructor
Julian
Jamie Barton
Instructor
Jamie
githubSource code

We’ll be using Prisma as our database ORM inside of our GraphQL resolvers. Prisma generates a “client” that we can use inside of our queries, and mutations to talk to our database.

We’ll be using 2 Prisma dependencies, for code generation, and use inside of our resolvers:

  • prisma
  • @prisma/client

At the terminal, run the following:

npm install -E -D prisma
npm install -E @prisma/client

Once installed, let’s run the Prisma initcommand to get started.

At the terminal, run the following:

npx prisma init

This will generate the file prisma/schema.prisma, and .env with some boilerplate.

Before we continue, let’s update package.json to include a script to run the prisma generate command.

"scripts": {
  "dev": "next dev",
  "build": "next build",
  "start": "next start",
  "lint": "next lint",
  "codegen": "graphql-codegen --config codegen.yml",
  "db:generate": "prisma generate"
}

We’ll next update .env to include the value of DATABASE_URL that points to our Docker instance we setup previously:

DATABASE_URL="mysql://root:password@localhost:3307/mydb"

Instead of the default postgresql database provider, we’ll use mysql. Inside prisma/schema.prisma update the datasource provider:

generator client {
  provider        = "prisma-client-js"
}

datasource db {
  provider             = "mysql"
  url                  = env("DATABASE_URL")
}

Now we’ll create an instance of PrismaClient that we’ll be able to use inside of our GraphQL API. In development we can exhaust our database connection limit very easily with reloading so we’ll attach it to a global object.

Create the file lib/prisma.ts and add the following:

import { PrismaClient } from "@prisma/client";

// PrismaClient is attached to the `global` object in development to prevent
// exhausting your database connection limit.
//
// Learn more:
// https://pris.ly/d/help/next-js-best-practices

declare global {
  var prisma: PrismaClient | undefined;
}

let prisma: PrismaClient;

if (process.env.NODE_ENV === "production") {
  prisma = new PrismaClient();
} else {
  if (!global.prisma) {
    global.prisma = new PrismaClient();
  }
  prisma = global.prisma;
}
export default prisma;

Now we’ll hook up Prisma with our GraphQL server context. Inside pages/api/index.ts go ahead and import the Prisma client we exported from lib/prisma.ts and the type PrismaClient from the @prisma/client:

import type { PrismaClient } from "@prisma/client";

import prisma from "../../lib/prisma";

Then define a new type for GraphQLContext:

export type GraphQLContext = {
  prisma: PrismaClient;
};

We should now update codegen.yml to point the contextType to our newly defined GraphQLContext type.

Inside of codegen.yml add a block for config under types.ts to the exported type:

overwrite: true
schema: "schema.graphql"
documents: null
generates:
  types.ts:
    config:
      contextType: ./pages/api/index#GraphQLContext
    plugins:
      - "typescript"
      - "typescript-resolvers"

Now if we run the codegen script we will have updated resolver context.

At the terminal, run the following:

npm run codegen

You’ll now see inside of types.ts that the type for Resolvers has been updated to include the GraphQLContext:

import { GraphQLContext } from "./pages/api/index";

// ...

export type Resolvers<ContextType = GraphQLContext> = {
  Cart?: CartResolvers<ContextType>;
  Query?: QueryResolvers<ContextType>;
};

Now inside of our resolvers we have fully typed context.

Before we continue, let’s create a function createServer object, and pass it along to createServer:

export async function createContext(): Promise<GraphQLContext> {
  return {
    prisma,
  };
}

const server = createServer({
  endpoint: "/api",
  schema: {
    typeDefs,
    resolvers,
  },
  context: createContext(),
});

At this point we’ve not created any models inside prisma/schema.prisma so running the script db:generate will not do anything. We’ll fix this next.

Before we continue let's add some additional "context" to our GraphQL Server by changing the default req/res types that the server uses to use the NextApiRequest and NextApiResponse types from Next.js.

Inside pages/api/index.ts add the following import:

import type { NextApiRequest, NextApiResponse } from "next";

Then where we invoke createServer, pass it the following:

const server = createServer<{
  req: NextApiRequest;
  res: NextApiResponse;
}>();