How to create a GraphQL API with Prisma and Nexus
Tags: graphql, prisma, api
Published: 2021-03-08 13:24:53

---
title: How to create a GraphQL API with Prisma and Nexus
published: true
description: A short tutorial that introduces you (and me) to creating a GraphQL api using modern tooling
tags: graphql, prisma, api
---

> 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](https://twitter.com/studio_hungry) and we can duke it out!

## What you will build

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.

### Prerequisites

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.

## What is Prisma and Nexus?

[Prisma](https://www.Prisma.io/) is an ORM (Object-Relational-Mapping) layer that sits on top of your database. It ships with a query builder which allows you to write type safe queries as JavaScript (or TypeScript) objects. It's built to work with TypeScript, has it's own vscode extension and offers a lovely abstraction over SQL databases. For a full (and in depth) rundown of what Prisma is you can check out their docs page, [Why Prisma?](https://www.Prisma.io/docs/concepts/overview/why-Prisma/).

[Nexus](https://nexusjs.org/) is a declarative, code first approach to writing GraphQL schemas. It's created by a team at Prisma and works seamlessly with it. Nexus allows you to house your schema and resolver logic in one place and write it with one common language such as JavaScript or TypeScript. You'll learn more about this approach during the tutorial. If you wish to read further you can checkout their excellent docs page [Why Nexus?](https://nexusjs.org/docs/getting-started/why-nexus).

## Setup the project

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.

```bash
npm init -y
npm i --save-dev prisma ts-node-dev typescript
npm i @prisma/client apollo-server graphql nexus
```

The article uses the following versions:

```json
  "devDependencies": {
    "prisma": "^2.17.0",
    "ts-node-dev": "^1.1.1",
    "typescript": "^4.1.5"
  },
  "dependencies": {
    "@prisma/client": "^2.17.0",
    "apollo-server": "^2.21.0",
    "graphql": "^15.5.0",
    "nexus": "^1.0.0"
  }
```

Next create a `tsconfig.json` file at your projects root and add the following boilerplate:

```json
{
	"compilerOptions": {
		"target": "ES2018",
		"module": "commonjs",
		"lib": ["esnext"],
		"strict": true,
		"rootDir": ".",
		"outDir": "dist",
		"sourceMap": true,
		"esModuleInterop": true
	}
}
```

Finally add some build scripts to the `package.json`

```json
"dev": "ts-node-dev --transpile-only --no-notify api/app.ts",
"build": "tsc"
```

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:

- api
  - graphql
    - index.ts
  - app.ts
  - context.ts
  - db.ts
  - schema.ts
  - server.ts

## Setup the database

This tutorial uses [Postgres](https://www.postgresql.org/) 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](https://www.postgresql.org/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.

```sql
// Login
psql -U postgres;

// Create database
CREATE DATABASE companyCompetenceDatabase;

// Confirm database creation
\l;
```

## Adding the Prisma schema data models

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.

The `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.

```bash
DATABASE_URL="postgresql://postgres:test1234@localhost:5432/companycompetencedatabase?schema=public"
```

> 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.

Inside `schema.prisma` add the following models below the client generator.

```prisma

// 1-n with trade and role
// company can have many roles
model Company {
  id            Int      @id @default(autoincrement())
  name          String?
  contactPerson String?
  bio           String?
  email         String?
  website       String?
  roles         Role[]
}

// 1-n with skill, n-1 with company
// a role can have many skills
// a company can have many roles
model Role {
  id        Int     @id @default(autoincrement())
  name      String?
  skills    Skill[]
  company   Company? @relation(fields: [companyId], references: [id]) // not in db, used by Prisma to set relation
  companyId Int? // the foreign key, this is in the db
}

// 1-n with role
model Skill {
  id     Int    @id @default(autoincrement())
  name   String?
  role   Role?   @relation(fields: [roleId], references: [id])
  roleId Int?
}
```

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.

In `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:

```prisma

model One  {
  id        Int @id @default(autoincrement()) // Primary key
  hasMany   Many[]
}

model Many  {
  id                Int @id @default(autoincrement()) // Primary key
  oneRelationField  One @relation(field: [foreignKey], references: [id]) // Not in db, used by Prisma to describe relation
  foreignKey        Int // The foreign key, this is in the db
}

```

The `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.

```bash
npx prisma migrate dev --name init --preview-feature
```

## Using the Nexus objectType

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:

```graphql

exports.typeDefs = gql`
type Query {
	allCharacters: [Character]!
	characterById(_id: ID!): Character
	characterByName(name: String!): Character
}

type Character {
	_id: ID
	name: String!
	house: String
	patronus: String
	bloodStatus: String
	role: String
	school: String
	deathEater: Boolean
	dumbledoresArmy: Boolean
	orderOfThePheonix: Boolean
	ministryOfMagic: Boolean
	alias: String
	wand: String
	boggart: String
	animagus: String
}
`
```

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 `objectType` function.

Inside `api/graphql` create a new file called `company.ts` and add the following:

```typescript
import { objectType } from 'nexus';

export const Company = objectType({
	name: 'Company',
	definition(t) {
		t.nonNull.int('id');
		t.string('name');
		t.string('contactPerson');
		t.string('bio');
		t.string('email');
		t.string('website');
		t.int('roleId');
		t.nonNull.list.nonNull.field('roles', {
			type: 'Role',
			resolve: (parent, _, ctx) => {
				return ctx.db.company
					.findUnique({
						where: { id: parent.id },
					})
					.roles();
			},
		});
	},
});
```

The `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.

```typescript
t.nonNull.list.nonNull.field('roles', {
	type: 'Role',
	resolve: (parent, _, ctx) => {
		return ctx.db.company
			.findUnique({
				where: { id: parent.id },
			})
			.roles();
	},
});
```

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 `api/GraphQL`

```typescript
// company.ts
export const Company = objectType({
	name: 'Company',
	definition(t) {
		t.nonNull.int('id');
		t.string('name');
		t.string('contactPerson');
		t.string('bio');
		t.string('email');
		t.string('website');
		t.int('roleId');
		t.nonNull.list.nonNull.field('roles', {
			type: 'Role',
			resolve: (parent, _, ctx) => {
				return ctx.db.company
					.findUnique({
						where: { id: parent.id },
					})
					.roles();
			},
		});
	},
});

// role.ts
export const Role = objectType({
	name: 'Role',
	definition(t) {
		t.nonNull.int('id');
		t.string('name');
		t.field('company', {
			type: 'Company',
			resolve: (parent, _, ctx) => {
				return ctx.db.role
					.findUnique({
						where: { id: parent.id || undefined },
					})
					.company();
			},
		});
		t.list.field('skills', {
			type: 'Skill',
			resolve: (parent, _, ctx) => {
				return ctx.db.role
					.findUnique({
						where: { id: parent.id || undefined },
					})
					.skills();
			},
		});
	},
});

// skill.ts
export const Skill = objectType({
	name: 'Skill',
	definition(t) {
		t.nonNull.int('id');
		t.string('name');
		t.field('role', {
			type: 'Role',
			resolve: (parent, _, ctx) => {
				return ctx.db.skill
					.findUnique({
						where: { id: parent.id || undefined },
					})
					.role();
			},
		});
	},
});
```

Finally, export the types from the `api/GraphQL/index.ts` file:

```typescript
export * from './company';
export * from './role';
export * from './skill';
```

## Creating the server

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.

### schema.ts

```typescript
import { makeSchema } from 'nexus';
import { join } from 'path';
// It is considered best practice to pass your types directly from a "star import" like we've done above. Under the hood, Nexus will unwrap the types. This prevents from constantly having to manually export & import every single type of your API.
import * as types from './graphql';

export const schema = makeSchema({
	// GraphQL types that will be used to construct your GraphQL schema.
	types,
	outputs: {
		// Output path to where nexus should write the generated TypeScript definition types derived from your schema. This is mandatory to benefit from Nexus' type-safety.
		typegen: join(__dirname, '..', 'nexus-typegen.ts'),
		// Output path to where nexus should write the SDL (schema definition language) version of your GraphQL schema.
		schema: join(__dirname, '..', 'schema.GraphQL'),
	},
	contextType: {
		// Path to the module where the context type is exported
		module: join(__dirname, './context.ts'),
		// Name of the export in that module
		export: 'Context',
	},
});
```

Open the `server.ts` file and add the following:

```typescript
import { ApolloServer } from 'apollo-server';
import { schema } from './schema';
import { context } from './context';

export const server = new ApolloServer({ schema, context });
```

Here you are instantiating the ApolloServer instance and exporting it while passing in the schema and context. Next you will create the context.

Finally open `app.ts` and add the following, which will start the server when the `dev` script is run.

```typescript
import {server} from './server';

server.listen().then(({url}) => {
    console.log(`Server ready at ${url}`)
})
```

## The GraphQL context

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 `api/db.ts` file.

```typescript
import { PrismaClient } from '@prisma/client';

export const db = new PrismaClient();
```

Then the `api/context.ts` file

```typescript
import { db } from './db';
import { PrismaClient } from '@prisma/client';

export interface Context {
	db: PrismaClient;
}

export const context = {
	db,
};
```

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.

## Writing queries and mutations

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 `Query` and `Mutation` types.

```typescript
export const Company = objectType({
	name: 'Company',
	definition(t) {
		t.nonNull.int('id');
		t.string('name');
		t.string('contactPerson');
		t.string('bio');
		t.string('email');
		t.string('website');
		t.int('roleId');
		t.nonNull.list.nonNull.field('roles', {
			type: 'Role',
			resolve: (parent, _, ctx) => {
				return ctx.db.company
					.findUnique({
						where: { id: parent.id },
					})
					.roles();
			},
		});
	},
});

export const CompanyQuery = extendType({
	type: 'Query',
	definition(t) {
		// get all companies
		t.list.field('companies', {
			type: 'Company',
			resolve(_root, _args, ctx) {
				return ctx.db.company.findMany();
			},
		});
		// get company by id
		t.field('company', {
			type: 'Company',
			args: {
				id: nonNull(intArg()),
			},
			resolve(_root, args, ctx) {
				return ctx.db.company.findUnique({
					where: { id: args.id },
				});
			},
		});
		t.list.field('roles', {
			type: 'Role',
			resolve(_root, _args, ctx) {
				return ctx.db.role.findMany();
			},
		});
	},
});

export const CompanyMutation = extendType({
	type: 'Mutation',
	definition(t) {
		// create a new company
		t.nonNull.field('createCompany', {
			type: 'Company',
			args: {
				id: intArg(),
				name: nonNull(stringArg()),
				contactPerson: nonNull(stringArg()),
				bio: nonNull(stringArg()),
				email: nonNull(stringArg()),
				website: nonNull(stringArg()),
				roleId: intArg(),
				roles: arg({
					type: list('RoleInputType'),
				}),
			},
			resolve(_root, args, ctx) {
				return ctx.db.company.create({
					data: {
						name: args.name,
						contactPerson: args.contactPerson,
						bio: args.bio,
						email: args.email,
						website: args.website,
						roles: {
							connect: [{ id: args.roleId || undefined }],
						},
					},
				});
			},
		});
		// update a company by id
		t.field('updateCompany', {
			type: 'Company',
			args: {
				id: nonNull(intArg()),
				name: stringArg(),
				contactPerson: stringArg(),
				bio: stringArg(),
				email: stringArg(),
				website: stringArg(),
				roleId: intArg(),
				roles: arg({
					type: list('RoleInputType'),
				}),
			},
			resolve(_root, args, ctx) {
				return ctx.db.company.update({
					where: { id: args.id },
					data: {
						name: args.name,
						contactPerson: args.contactPerson,
						bio: args.bio,
						email: args.email,
						website: args.website,
						roles: {
							connect: [{ id: args.roleId || undefined }],
						},
					},
				});
			},
		});
		// delete a company by id
		t.field('deleteCompany', {
			type: 'Company',
			args: {
				id: nonNull(intArg()),
			},
			resolve(_root, args, ctx) {
				return ctx.db.company.delete({
					where: { id: args.id },
				});
			},
		});
	},
});
```

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.

```typescript
// get all companies
t.list.field('companies', {
	type: 'Company',
	resolve(_root, _args, ctx) {
		return ctx.db.company.findMany();
	},
});
```

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:

```typescript
// get all companies
t.list.field('companies', {
	type: 'Company',
	resolve(_root, _args, ctx) {
		return ctx.db.company.findMany({ take: 10 });
	},
});
```

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, `args`.

```typescript
// get company by id
t.field('company', {
	type: 'Company',
	args: {
		id: nonNull(intArg()),
	},
	resolve(_root, args, ctx) {
		return ctx.db.company.findUnique({
			where: { id: args.id },
		});
	},
});
```

Here a different method is used on the GraphQL Prisma context, `findUnique()`. A filter is passed which asks for only companies `where` it's `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.

```typescript
        // create a new company
        t.nonNull.field('createCompany', {
            type: 'Company',
            args: {
                id: intArg(),
                name: nonNull(stringArg()),
                contactPerson: nonNull(stringArg()),
                bio: nonNull(stringArg()),
                email: nonNull(stringArg()),
                website: nonNull(stringArg()),
                roleId: intArg(),
                roles: arg({
                    type: list('RoleInputType')
                })
            },
            resolve(_root, args, ctx) {
                return ctx.db.company.create({
                    data: {
                        name: args.name,
                        contactPerson: args.contactPerson,
                        bio: args.bio,
                        email: args.email,
                        website: args.website,
                        roles: {
                            connect: [
                                {id: args.roleId || undefined}, 
                            ]
                        }
                    }
                })
            }
        })
```

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 `list`. The `RoleInputType` is defined in the `role.ts` file and defines an `id`. 

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.

## Running our queries and mutations in the Playground

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 `console.logs`.

Begin by creating a new skill.

```graphql
mutation CreateSkill {
  createSkill(name: "GraphQL") {
    id
    name
  }
}
```

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.

```graphql
mutation CreateRole {
  createRole(name: "Backend Developer" skillId: 1) {
    id
    name
  }
}
```

The `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 `GraphQL` skill.

```graphql
mutation CreateCompany {
  createCompany(
    name: "My Super Cool Company"
    bio: "A super cool company"
    website: "mycomp.com"
    contactPerson: "Barry McBarry"
    email: "barry@mycomp.com"
    roleId: 1
  ) {
    name
    bio
    website
    contactPerson
    email
    roles {
      name
      skills {
        name
      }
    }
  }
}
```

Now that your database has some data in it you can query it to see how that data looks when returned.

```graphql
# get all companies and their roles and skills
query GetCompanies {
  companies {
    id
    name
    contactPerson
    bio
    email
    website
    roles {
      name
      skills {
        name
      }
    }
  }
}

#get all roles and their skills
query GetRoles {
  roles {
    id
    name
    skills {
      name
    }
  }
}

#get all skills
query GetSkills {
  skills {
    id
    name
  }
}

```

## Conclusion

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](https://twitter.com/studio_hungry) if you fancy a chinwag about any of this.