Robert Cooper
blogprojects
Moon

Upgrading from Prisma 1 to Prisma 2

This post outlines the following:

  • Steps to take to upgrade a Prisma 1 app (built with nexus-prisma) to Prisma 2
  • Benefits of upgrading to Prisma 2
  • Considerations to keep in mind regarding the upgrade
  • Whether upgrading to Prisma 2 is worth it

Steps to take to upgrade

Clear node modules

bash
1rm -r node_modules

Clearing the node_modules directory eliminates any issues with existing code found in the node_modules causing problems with the new prisma and nexus libraries that will be added.

Remove existing prisma and nexus dependencies

bash
1yarn remove prisma prisma-client-lib nexus-prisma-generate
  • prisma was the Prisma 1 CLI. Prisma 2 uses @prisma/cli.
  • prisma-client-lib was the Prisma 1 client. Prisma 2 uses @prisma/client
  • nexus-prisma-generate was used to generate code that was used to create a GraphQL schema. The latest nexus relies on @nexus/schema to generate the schema.

Upgrade nexus dependencies

bash
1yarn upgrade nexus@latest nexus-prisma@latest

Install new prisma and nexus dependencies

1yarn add @nexus/schema @prisma/client @prisma/cli
  • @nexus/schema includes the functions required to build the nexus GraphQL schema. The functions provided by @nexus/schema will be enhanced with prisma-specific methods with the usage of the nexusPrismaPlugin, which is imported from nexus-prisma.

The new nexus documentation is at nexusjs.org and the old nexus documentation is at nexus.js.org. Nexus is moving towards becoming a framework, so that is likely why there is a completely new set of documentation for the nexus.

  • @prisma/client is the Prisma 2 client.
  • @prisma/cli is the Prisma 2 CLI which will be used for introspecting the database as well as generating the Prisma client.

The Prisma 2 CLI can also be used for database migrations, but it is still in an experimental phase so it's probably a safer bet to rely on another tool for your database migrations, such as Knex.js.

Create new datamodel file

Prisma 2 uses a new datamodel. The datamodel contains information about the structure of the database tables.

Create a schema.prisma file in the project root:

yaml
1generator client {
2 provider = "prisma-client-js"
3}
4
5datasource db {
6 provider = "mysql"
7 url = "mysql://root:rootPassword@localhost:3306/dbName"
8}

The datasource configuration will be different depending on the type of database you use. You can also use environment variables for the url config by using the env() function in the datamodel file. The prisma CLI will automatically try to load environment variables found in a .env file and use those to replace the environment variables specified in the datamodel file.

yaml
1generator client {
2 provider = "prisma-client-js"
3}
4
5datasource db {
6 provider = "mysql"
7 url = env("DATABASE_URL")
8}

With Prisma 1, it was possible to specify the environment variable filename for the prisma deploy command. With Prisma 2, there is no way to specify the filename used for the environment variables when running the command to generate the Prisma client.

Introspect current database

bash
1npx prisma introspect

Introspecting the database will populate the schema.prisma with models that are based off your database tables and columns. You'll need to manually adjust some of the model fields for some of the information the introspect command is not able to know about. Here are some things you may need to manually adjust:

  • All the createdAt fields need to have an attribute of @default(now())
  • All the upadtedAt fields need to have an attribute of @updatedAt
  • All id fields need to have a default value added to them @default(cuid())
  • All enums must be added manually and the fields that use the enums need to be adjusted

Generate the prisma client

bash
1npx prisma generate

This will generate the prisma client inside of the node_modules directory. The prisma client can now be imported and used anywhere:

tsx
1import { PrismaClient } from @prisma/client;
2
3const prisma = new PrismaClient();

Try to instantiate the least number of PrismaClient instances to minimize the number of connections to your database.

Create schema with nexus

tsx
1import { makeSchema } from '@nexus/schema';
2import { nexusPrismaPlugin } from 'nexus-prisma';
3import { Query } from './queries';
4import { Mutation } from './mutations';
5
6export const schema = makeSchema({
7 shouldGenerateArtifacts: true,
8 types: [
9 Query,
10 Mutation,
11 ],
12 plugins: [
13 nexusPrismaPlugin(),
14 ],
15 // Tells nexus where to look for types when generating the graphql schema
16 typegenAutoConfig: {
17 sources: [
18 {
19 source: '@prisma/client',
20 alias: 'prisma',
21 },
22 ],
23 },
24 // Tells nexus where to output the generated graphql schema and types
25 outputs: {
26 schema: path.join(__dirname, '../generated/schema.graphql'),
27 typegen: path.join(__dirname, '../generated/nexus.ts'),
28 },
29});

Previously you would have had to import makePrismaSchema from nexus-prisma, but now you import makeSchema from @nexus/schema.

In order to take advantage of the prisma plugin for nexus, nexusPrismaPlugin needs to be imported from nexus-prisma and added to the plugins array.

Create and configure the GraphQL server

tsx
1import { GraphQLServer } from 'graphql-yoga';
2import { schema } from './schema';
3
4const prisma = new PrismaClient();
5
6const server = new GraphQLServer({
7 schema,
8 // Adds prisma to the graphql context for use inside of mutation and query resolvers
9 context: {
10 prisma,
11 }
12});

The above example uses graphql-yoga, but it should just as easily be done with Apollo Server. All you need to do is pass the schema to the graphql server configuration and also add prisma to the graphql server's context.

You may want to consider trying out the Nexus framework, which includes its own server. You would need to install nexus@next and follow this migration guide to get things setup.

Adjust all queries and mutations

Now it is required to adjust all the queries and mutations that have been defined using the "old nexus" with the "new nexus". Here is what should be changed:

  • Imports of stringArg, idArg, intArg, arg, etc from nexus should now be imported from @nexus/schema
  • prismaObjectType does not exist anymore. Instead, you should use mutationType for mutations, queryType for queries, and objectType for other schema objects. mutationType, queryType and objectType can all be imported from @nexus/schema.
  • Any prisma model you would like to expose need to be exposed by using objectType and t.model. t.model is used to specify which fields on your Prisma model you wish to expose.

For example given the following Prisma model:

yaml
1model User {
2 id String @default(cuid()) @id
3 email String @unique
4 hasVerifiedEmail Boolean?
5 password String?
6 resetToken String?
7}

You could choose expose only the id, email, and hasVerifiedEmail fields through your API by defining the User object type as follows:

tsx
1import { objectType } from '@nexus/schema';
2
3export const User = objectType({
4 name: 'User',
5 definition(t) {
6 t.model.id();
7 t.model.email();
8 t.model.hasVerifiedEmail();
9 },
10});
  • Make sure to add all the objects, queries, and mutations defined with @nexus/schema to the types property of the makeSchema function in order to have all the object types, queries, and mutations added to the generated schema.
tsx
1export const schema = makeSchema({
2 ...
3 types: [
4 // Object Types
5 BillingInfo,
6 Company,
7 JobApplication,
8 Resume,
9 User,
10
11 // Queries
12 Query,
13
14 // Mutations
15 Mutation,
16 ],
17 ...
18});
  • The generated prisma client is slightly different than the prisma client generated with Prisma 1, so rely on TypeScript to tell you which methods/arguments need to change when using the prisma client in your resolvers.

Start the server

bash
1ts-node --files src/index.ts

Now, all there is left to do is to try starting you server and if all goes well, the server will running and nexus will generate all the types for the api in addition to the graphql schema. The location of the generated nexus code is determined by the outputs configuration of the makeSchema function.

During the migration, I recommend you comment out your existing queries and mutations defined with nexus and migrate them one at a time. You should regularly try to generate your nexus schema with the newly written migration queries and mutations to make sure no errors exist.

Instead of having to start you server to generate your schema, consider moving the code that generates the schema in it's own file and then run a command to execute that file. That command would like something like:

bash
1ts-node --transpile-only src/schema

Read more about the recommended project setup in the Nexus docs.

Benefits of upgrading to Prisma 2

  • No longer need a seperate server for Prisma. This simplifies a lot of things, from deployments to local development.
  • VSCode extension exists for the latest datamodel, unlike the old Prisma 1 datamodel.
  • Prisma 2 is actively maintained and developed, unlike Prisma 1. The main documentation for Prisma is now all for Prisma 2 and you will likely end up on a lot of broken links when trying to access documentation for Prisma 1 through search engines.

Considerations to keep in mind

You'll have to get comfortable with manipulating databases with SQL when using Prisma 2. Unless you want to try out the experimental Prisma Migrate, you'll need to setup a tool to handle database migrations, such as Knex.js. You can use the prisma introspect command to help figure out how changes to your database are reflected in the prisma datamodel. However, when running the introspection command, any custom modifications previously made to the datamodel file (such as specifying default values) will be erased, so they will need to be added back manually.

I highly recommend that your Prisma 1 project is written in TypeScript before trying to attempt the upgrade to Prisma 2. TypeScript will help guide the migration by indicating what needs to be fixed and makes the upgrade much more manageable. Trying to upgrade without TypeScript would be incredibly difficult to do without introducing a lot of new bugs.

Any sort of deletion behaviour (such as cascading deletes) needs to be implemented at the database level. This means that SQL commands need to be run against your database in order to alter foreign key constraints. Prisma has some documentation on how to setup foreign key constraints, but it does not specifically talk about altering existing foreign key contraints, which is what you will need to do.

You'll likely encounter some other challenges during the migration, since it is a significant upgrade. For reference, here are some issues i've encountered and had to work around when upgrading:

Is upgrading to Prisma 2 worth it?

You should consider upgrading to Prisma 2 if:

  • You plan to continue active development of a project that is currently using Prisma 1
  • You want to eliminate the prisma server to save infrastructure costs
  • You want to take advantage of the latest features of Prisma 2
  • Willing to accept you will need to fix a few new bugs introduced from the migration of Prisma 2

You should avoid upgrading to Prisma 2 if:

  • Your Prisma 1 project is written in JavaScript. I would first migrate it to TypeScript before attempting to upgrade.
  • Your app only needs occasional maintenance. The upgrade does require a significant amount of time and will likely introduce a few bugs, so it's not worth doing if your app is not actively being developed.
  • You don't mind paying for the additional infrastructure required to run the prisma server that is required with Prisma 1

If you'd like to see some examples of projects that use Prisma 2, check out these resources:


Next:How to write a commit message that will make your mom proudPrevious:Deploy an app to multiple environments with Vercel

Hey, I'm Robert Cooper and I write articles related to web development. If you find these articles interesting, follow me on Twitter to get more bite-sized content related to web development.


Join the Newsletter

Sign up to my newsletter to stay up to date with my latest articles.
  • Github
  • Twitter
  • Email
  • RSS