As part of my learning everything there is to know about Prisma I've decided to write some tutorials for you as much for me. There may be some knowledge gaps and I may not have a great grasp on some things yet, so if you find an error or something that I have explained that is fundamentally wrong, feel free to message me on Twitter @studio_hungry and we can duke it out!
You are going to build a GraphQL api backed by a postgres database. This api will hold data for showing different competencies within companies. For example, each company will have roles within their organization (Software Engineer, Cloud Specialist) which require certain skills (Scala, React).
Once complete you will have learnt how to setup your own GraphQL api, how to use Prisma as an ORM layer and how to use Nexus as a means of producing code first GraphQL schemas.
The complete project code can be found at company-competence-graphql-api
This tutorial assumes basic knowledge of databases, APIs and how to setup a project and install packages. The tutorial borrows heavily from the awesome docs for Prisma and Nexus.
Helpful to know
Create a new directory in your project location of choice and begin by installing the required packages.
This tutorial will use NPM to run the application, you can use Yarn if you wish.
The article uses the following versions:
Next create a
tsconfig.json file at your projects root and add the following boilerplate:
Finally add some build scripts to the
Now that everything is installed you'll need to create some files and folders. All the code will live in an
api folder at the projects root. The structure will look like this:
This tutorial uses Postgres as it's database. If you don't already have an instance of Postgres installed on your machine you can grab it from the download page on their site. The default postgres installation comes with the user
postgres. This tutorial will assume that user from here onwards, though you can swap that out for your own user if you wish. This tutorial will also use
psql for interacting with the database at certain times.
psql is a tool for writing SQL queries from the command line, it comes with the installation of Postgres.
Once you have it installed login and create a new database.
To setup Prisma you must first invoke the CLI by running
npx prisma then
npx primsa init. A new folder will be created at the projects root called
prisma which will house a
schema.prisma file. A
.env file will also be created and pre-populated with a placeholder connection string.
DATABASE_URL is the connection string that will be used to hook up to your Postgres database. In the
.env file change the placeholder to the following, replacing the password for your own, the user for your own if you have a different one and the database name if you choose a different name.
Note that although you gave the name of the database in camelCase when creating it, Postgres converts that to all lower case once the database is created. I have left the creation string as camelCase for readability.
schema.prisma add the following models below the client generator.
Let's dive into what all this means. The Prisma data modal is a schema that maps to your database schema. In it you define the shape of your data and any relationships that may exist between tables. If you have never worked with
SQL databases some of fields may seem confusing. Let's break them down.
SQL land relationships between tables are defined by foreign keys. These are ids that map to other tables. In the example above you can see that the
Role model has a field called
companyId, this is a foreign key that will be added to the database and represents the link to the primary key (the id field) in the
Company model. In the
Company model you can see an array of roles. This represents a collection or list of roles, the many in the 1-many relationship that the
Company (1) and
Roles (many) can have. The
? next to many of the field denotes that they are nullable.
A more explicit example of a one-many relationship can be viewed below:
foreignKey references the id on the
One model, this tells the database that the primary key from
One is used as the foreignKey on
Many. The foreignKey is designed to allow multiple instances of the value from which it references. Only the foreign key is used in the actual database table, the
oneRelationField is used by Prisma when creating relations.
Now that you have created your data model you can run a migration and map the models to the database schema. The following command creates a new SQL migration file named
init and runs the migration against the database. This creates the tables in the format described by the models.
The traditional way to setup a GraphQL schema is to build a schema using the
GraphQL Schema Language which is written as a template string. A resolver is also created to resolve the endpoint and return whatever data is requested. In Apollo this is done by creating a
typeDefs file and declaring the schema types and queries. Inside the
typeDefs file you would create the the GraphQL types (which look quite similar to the Prisma Schema Language (PSL)) and queries inside an exported template string on a GraphQL tag, an example how this might look can be seen below:
This file would then be added to every time you wanted to introduce a new data type or query definition, this works so long as the number of data models is small but is not very modular and wouldn't be easy to scale if you were to introduce multiple new data types and queries. Another way to accomplish defining your GraphQL types and schema is to use Nexus and to take a code first approach by giving each type its own file and using the
api/graphql create a new file called
company.ts and add the following:
objectType function is used to fetch the fields from your schema. It's combined with a resolver and describes the schema models in code. The
definition method allows you to define the fields of your object type as well as the relationships.
Let's dissect the relations field to make sure we understand what is happening here.
The field is not nullable, that is, it can't be null
t.nonNull, it's an array (a list) of values which can also not be null,
t.nonNull.list.nonNull. In the field you have given it a name that corresponds to the name used in the schema and set it's model type,
Role, you then resolve the relationship. The first argument in the resolver is the parent or root node, this is used to pass information from the parent to the child resolvers. The second is the arguments passed to the resolver and the third the schema context, which we will shortly be covering. The Prisma client (via the context) is used to query for the model and find the record where the id matches. So in this case the query will resolve to find zero or one Company that matches the filter.
The full contents of
Finally, export the types from the
Begin by opening
api/schema.ts. This will be where you make a schema with Nexus. You will pass in the defined types from the
api/GraphQL/index.ts file and set the output paths. These paths will be where Nexus writes the generated TypeScript definitions that are derived from the Prisma schema. It will also output a GraphQL schema, this file should not be edited directly.
server.ts file and add the following:
Here you are instantiating the ApolloServer instance and exporting it while passing in the schema and context. Next you will create the context.
app.ts and add the following, which will start the server when the
dev script is run.
The GraphQL context is an object which is shared across all the resolvers, the GraphQL server creates and destroys a new instance between each request. To create a context open the
Now the context has been created and passed to the GraphQL server. The context can be used to store and/or check information too. One such use case might be an authenticated user, are they authorized to do a certain mutation? In any case, your context is a simple object which will suffice for now.
The API isn't much good if you can't make any requests against it. Begin by opening back up the
companies.ts file. Under the
objectType creation of the
Company type, add the new
Let's dissect that massive block of code. Starting with the
Query block, the queries available to you in the playground are defined using much of the same syntax as when the object itself was defined with fields and resolvers.
The query to get all of the companies uses the GraphQL context to run a prisma query and the method
findMany() to return all of the companies in the database. The
findMany() method can take optional parameters which are filters on the query, for example to return only the first 10 company in the database you could pass an object with the key
take and the value
10 like so:
This value could be passed into the query as an argument too instead of being hardcoded. Let's look at passing arguments to queries.
To get a company by it's
id you'll make use of the second paramter of the resolver function,
Here a different method is used on the GraphQL Prisma context,
findUnique(). A filter is passed which asks for only companies
id matches the
id passed in as a parameter. Above the resolver the args types are defined. Here an
id is defined as a nonNull integer, that is, a number value that cannot be null. Which makes sense as you wont run this query without actually passing a value to it. If you were to not add the nonNull function the arg would be nullable be default. (Though you can also import nullable from nexus and explicitly state that fact that it is nullable). The roles query works the same way as the get all companies query in that it returns all of the roles related to this company.
Moving onto the mutations and creating a new company in the database.
Much of this code will now be familiar, the field type is given a logical name (which will be used to run the mutation) and a bunch of args are defined outlining what the resolver can expect and in what format the variables will be passed as. An interesting point to notice here is the
roleId, which is the foreign key for the
Role type, and the
roles arg which uses a custom input type to tell the resolver what type to expect. As the
Role isn't a primitive you have to declare a custom type which is used instead. As the plural name suggests, this argument is of type
RoleInputType is defined in the
role.ts file and defines an
The resolver uses the create method on the Prisma context and defines a
data object which maps the args passed through to the corresponding object keys. The roles arg uses the
connect method and the foreign key to connect the new company to a previously defined role.
In order to actually play about with the queries and mutations run the
dev script from the projects root (
npm run dev). This will open up a url at localhost:4000. The server has given you a GraphQL playground from which you can run your queries and mutations against the database in real time. This is an excellent way to check how your data behaves in the real world, outside of those
Begin by creating a new skill.
Here you are running your
createSkill mutation passing in the name of the skill top be created and returning the newly created skills id and name. Next create a role.
createRole mutation accepts a
name argument and an optional
skillId, which is the foreign key to the skill you would like to link to the role. In this case you are creating a new
Backend Developer role and assigning it the skill of
GraphQL via the skills id.
Finally you can create a new company. By taking advantage of the foreign key (
roleId) you can link not only a role but certain skills to the company. In this mutation your new company has a
Backend Developer role which requires/uses a
Now that your database has some data in it you can query it to see how that data looks when returned.
As a predominantly frontend developer using Prisma and combining it with Nexus for the first time has been a relatively painless experience. Of course much of that is due to the excellent documentation which allows you to get up and running quickly and without fuss. Some areas where I did find myself rather confused was nested mutations and relations in the schema. People learn in different ways, I enjoy quite a lot of visuals with pictures forming a better understanding in my head as opposed to just blocks of text. Perhaps the docs could do with some more video instructions and interactive elements. That being said this was quite a text and code block heavy piece itself!
This was the first of many pieces about my journey learning Prisma and what it can do. This article only touched on some base concepts but I found it very helpful in getting a grasp of what is possible. My take away from this piece is that I need to dive deeper into relationships and how they are used as this seems to be a focal point of what you can do with Prisma.
Hit me up on Twitter @studio_hungry if you fancy a chinwag about any of this.