Let’s Create A Nest, Nx, GraphQL, Prisma Single Data Model Definition
By: Kristan 'Krispy' Uccello
Tags: GraphQL, Nodejs, Software Development, Programming, Prisma

Every framework and library seems to have its own SDL format for data models. This leads to model definition duplication at best and complete and utter chaos at worst.

This article details one approach that is fairly simple and useful when you have both libraries and applications that need to share data models across business domains.

The selected DMF (Data Model Format) is Prisma’s schema and from this schema I show you how it can manage your SQL DB model, API Server and React Client data model as well as how to bind the model to a generated GraphQL service with app level control of exposed model properties. It sounds more complicated than it is.

With that stated, let’s dive in!

Part 1. What we’re doing

To state the goal of this again, what we are trying to do is setup our project workspace such that we have a single node library that holds the data model and any application that imports that data model library can quickly and easily setup and expose a GraphQL api endpoint with the parts of the model it chooses to manage. The desire is to have a single definition of what the data model is and have all the “implementing systems” use that singular model definition. This will allow us to avoid data model definition duplication and simplify some aspects of our data management.

To make this a complete example I will detail some basic setup for an Nx workspace (not comprehensive). Read up on Nx here. For this example setup, we will use an sqlite3 database but in production you will probably want to use postgres or similar. Also, note that the data model we will use for this example is a bare minimum model borrowed from the Prisma example schema slightly modified to run on Sqlite (removed the enum for Role).

Part 2. Setup workspace

First step is to setup our directories, create the workspace, generate the skeleton app and lib and then install the needed npm modules.

At this point we have a bare skeleton of a workspace setup. We will need to add some tasks to our workspace to simplify our data model migration steps. Lets do that now — in the project directory open the file named workspace.json

Have a look at lines 85–125 above. I added these tasks to wrap the Prisma migrations. It is a preview feature (previously — like last week at the time of writing this post — it was in experimental mode so its likely that in the near future it will be a full feature).

Now, as I mentioned, for this example project we will use sqlite — you should open the file <project-root>/libs/data-model/prisma/schema.prisma

Here is what my file looks like:

With this file in place and the changes made to our workspace.json all you need to do now is modify the <project-root>/libs/data-mode/.env file to have the following contents which will tell our Prisma schema what the value should be for the environment variable it uses:

DATABASE_URL="file:./dev.db"

Now, run the following from the project root:

nx run data-model:migrate-up

When you execute this task you should see something like:

> nx run data-model:migrate-up

Environment variables loaded from .env

Prisma schema loaded from prisma/schema.prisma

Datasource "db": SQLite database "dev.db" at "file:./dev.db" SQLite database dev.db created at file:./dev.db The following migration(s) have been created and applied from new schema changes: migrations/

└─ 20201214205941_/

└─ migration.sql ✔ Generated Prisma Client (2.13.0) to ./../../node_modules/@prisma/client in 97ms

You can check the status of this by running:

> nx run data-model:migrate-status

Environment variables loaded from .env

Prisma schema loaded from prisma/schema.prisma

Datasource "db": SQLite database "dev.db" at "file:./dev.db" Status

1 migration found in prisma/migrations Database schema is up to date!

At this point, you might be inclined to write a small script to seed your database, and you would be right to do so but in our example we will skip that and setup a GraphQL mutation so that way we can insert and read data using the api. Here is the part we have setup so far:

What we have setup so far in our example workspace

Here is what my workspace looks like directory & file wise:

Part 3. Wire up our Prisma schema and expose a GraphQL API

Now comes the meat of our example — setting up automatic generation of a rich GraphQL api from the Prisma schema and client. For this example I am using NestJS (though you could use another framework or simply roll your own approach — milage may vary). Remember that app we created when we first setup the project workspace? Ya, that app the one called “my-api”.

Before we jump into the code there are some additional libraries we should install:

npm i @nestjs/graphql @nexus/schema@0.19.2 graphql-scalars apollo-server-express --save

Quick side note — if you see an error that says something about a type mismatch at any point like this:

The types of 'config.fieldDefTypes' are incompatible between these types.

It means you may have an incompatible (or broken) version of the nexus plugin prisma node nodule. For this example I am using:

"nexus-plugin-prisma": "^0.26.0" and "@nexus/schema": "^0.19.2"

It turns out that 0.27.0 is broken at the moment. Just be aware.

Ok, so now let’s do the fun part of writing some actual code. Open the app.module.ts file in the <project-root>/apps/my-api/src/app directory. Change the file so it looks like:

The app module imports the GraphQL library from nestjs and defines that the configuration for the graphql api (the schema, types and resolvers) comes from the class GraphqlConfigService. This service class is what we will create next.

Create a new file in the same directory called gql.config.ts and it is in this file that the magic happens. Here is what you should place in the source file:

Now there is a lot going on here. The TL;DR; summary is that this class is defining an injectable configuration for the GraphQL api based on what is available in the generated prisma client that was created when we “built” the data model from the schema.

The first thing to note is we are using the nexus/schema module to make a schema based on the config object we provide. The config object includes the plugin nexus-plugin-prisma (see line #32). We pass a config to the plugin telling it to generate crud operations — which come from the generated Prisma client code based on the schema.prisma we setup in the library earlier.

The second thing of note is where we define the types (lines #36–78) that the application will make use of along with a set of queries and mutations that we want the app to expose. This is a great way to separate concerns and while there is a slight duplication of whats in the data model schema this is forgiven because of the ability to selectively choose what the graphql api will expose from that model definition.

This is the section we just completed

We are now ready to insert data using the GraphQL API we just setup. Lets start the server and insert our first user.

nx serve my-api

You should see some output in your console telling you the server is listening on http://localhost:3333/api — open a browser and go to http://localhost/graphql and you will be presented with the graphql playground. Lets create a new user:

Now that we have created some users we can query for them as well

You can swap the db for a postgres or mysql one (maybe use a docker container or some other service provider). You are only limited by what Prisma supports and even then, there is nothing stopping you from creating an adapter for a currently unsupported database (maybe Big Query? or Elastic?)

There is a lot you can do with this — while I would like to go deeper here, this post is already very long. I must stop writing more and trust that what we have covered makes sense (clap for it and or leave a comment if this was useful to you).

So where do you go from here? Try these resources: