ORMs (Object Relational Mappings) get a ton of hate amongst engineers today. We often look at ORMs with disdain writing them off as “leaky abstractions” over our database. But time and time again, engineering teams still use ORMs! Often they start with an ORM and use them with full force until they start hitting performance issues. Once the bottlenecks hit, many teams fork their current library and add more abstractions on top of it to fit their business need! I think this is because most ORMs abstract naive database operations very well, but leave users to step down into native APIs to handle more complex scenarios.
Martin Fowler addressed this “hate” on ORMs in his post here. I do think the solution he proposes makes sense: own the relational model all the way through your application.
With Prisma, we do just that.
We describe our database models with the GraphQL SDL (Schema Definition Language) and then map this data model to a GraphQL API compliant with the Open CRUD specification. These APIs represent the “data access layer” sitting above your database (SQL, Postgres, Mongo) with the responsibility of making efficient queries against your database. Effectively we have created a “GraphQL ORM” for your database.
Let’s see how this plays out.
We define a type
User using the SDL syntax.
Prisma will then generate the GraphQL API below. I’ve omitted the input types just to show off the core APIs exposed.
As you can see for our type User, we have generated a GraphQL Schema describing Query, Mutation, and Subscription operations. Now we can send GraphQL queries to the data access layer to interact with our database!
Now that we have a general idea of how Prisma abstracts our database, let’s revisit ORMs with a new example.
Prisma Client represents an ambition. How can we access our database with an ORM-like experience without an actual ORM.
Enter Prisma Client.
We’re going to build out a simple REST API with Prisma to show off the experience of using Prisma Client. The architecture should follow the diagram below.
First let’s bootstrap a new project.
$ mkdir prisma-rest-example
$ cd prisma-rest-example
Next let’s install the Prisma CLI globally on your machine via NPM.
$ npm install -g email@example.com
Let’s put our Prisma artifacts in a
$ mkdir prisma
We need three things to get a Prisma API started
- datamodel.prisma — Your database model expressed in SDL syntax
- prisma.yml — Configuration of your Prisma server
- docker-compose.yml — Docker compose file describing the Prisma server image and your database image.
$ cd prisma
$ touch datamodel.prisma
Paste the following datamodel into your
We’re going to use a simple example. A User with posts and a Post with an author.
$ touch prisma.yml
Paste the following configuration into the
Let’s break down what is in this file.
endpoint— the remote url for the Prisma server
datamodel— the datamodel.prisma file
generate— The generator for our Prisma Client
generate field is the interesting one here. When the Prisma CLI runs
generate, it will output a Prisma Client API based on a generator. In our example here we are using the
typescript-client, but you could use a variety of clients like
More client generators will be added as engineers from different language communities adopt Prisma Client!
$ cd ..
$ touch docker-compose.yml
Paste the following content into your
docker-compose.yml file. This file is located in the root of your project.
Here we setup two services: the Prisma base image and MySQL.
Running the server
Now that we have our configuration files in place, let’s deploy a Prisma server.
First in the root of your project, start your containers with
$ docker-compose up -d
This will pull the public images for
mysql and run them on our machine via docker.
Next we need to deploy our
datamodel to our server. Execute the commands below.
$ prisma deploy
You can see that the Prisma server has taken our
datamodel and generated the GraphQL API.
Let’s open up the GraphQL Playground to interact with our database.
Here we create a new User in our database using the
createUser API generated from our
datamodel. We can query this User now!
Generating the client
Okay so now that we have a data access layer on top of our database, the next thing we need to do is generate a client we can use in the application tier.
To generate a Prisma Client execute the command below.
$ prisma generate
This will generate a
prisma-client folder in your
Let’s setup our REST API and we will revisit the client shortly.
First thing let’s bootstrap our
$ yarn init -y
Next in the root of our project let’s install some dev-dependencies.
$ yarn add typescript ts-node -D
Let’s setup a
tsconfig.json in the root of our project.
$ touch tsconfig.json
Copy the following content into your
To use our client we need to install
graphql and the
$ yarn add firstname.lastname@example.org graphql -S
$ yarn add @types/graphql -D
Now we need to install a web server, we’ll use Express.js.
$ yarn add express body-parser -S$ yarn add @types/express @types/body-parser -D
Next create an entry point to the server.
$ cd src
$ touch index.ts
Let’s add a
start script to our package.json.
"start": "ts-node ./src/index.ts"
Time to scaffold our endpoints. Copy the following content into your
Creating a User
First we need to import the client from
./src/generated/prisma-client and then we can implement the
This is shown below.
Here we get the input arguments from the request body, use the Prisma Client much like an ORM and use the
createUser method. We’ll respond
200 and return the newly created
Fetching a User
Next we can implement a query to find a User by a specific ID.
Just like creating a
User, the client has a simple approach to fetching a
Let’s see if everything works. Open up your terminal in the root of this project and type:
$ yarn start
Now we can open up POSTMAN or a similar tool to make a request.
Sweet that worked! Let’s fetch the newly created
Awesome! You can see how this is a nice developer experience for interacting with your database.
Creating a Post
Now we’ll implement creating a post.
This is where things get a bit more interesting! If we remember the
Post type in our
datamodel has an
author field. This is a relation to the
User. So when we create our
Post we use the
connect syntax to “connect” this
Post to a
Publishing a Post
So we just implemented creating “draft” posts. Let’s implement the endpoint that makes the
published field on a
Here we use the
updatePost method which has 2 requirements. First a selector for the item using the
where syntax. Last, a
data object describing the change. We find a
Post where the
id matches the
id of our request and change the
published field to true. Sweet.
Fetching a Post
This looks identical to fetching a user.
And we can test it.
But wait! Our Post type specifies an
author field as well. By default queries against a type does not resolve relations. To fetch the author as well, let’s modify our code.
We can access the relation by calling the
author method once we have a post!
Fetching Posts/Draft Posts
I hope as we are going down the list of endpoints, implementing each one is getting easier for you to understand how easy to use Prisma Client is! Let’s finish up these APIs.
Here we just use the
posts method on the client to fetch posts whether published is true or not. We could further get the authors for each post by mapping the posts array to fetch each
author field but we have omitted that here to keep this example simple.
Using the Prisma Client has been a really great experience for building CLIs, GraphQL Servers, REST APIs, Webhooks, you name it. I love that it abstracts the annoyances of data access and gives you a nice API client with a pretty expressive syntax! We used typescript in this example, but you can generate the client in your favorite languages and start integrating it into your stack.
The source code for this blog post is located below.