Setting Up A Graphql Server With Node + Graphql-Yoga + Prisma
What you will learn
- Introductory knowledge on GraphQL
- How to setup GraphQL server with GraphQL-Yoga
- How to setup Prisma and query your database
- Build a mini note taking application with CRUD functionalities
What is GraphQL
GraphQL is a query language for api that helps to unify data requirements from multiple clients which can be mobile, web, desktops and a long range of other devices.
Prior to GraphQL there was REST which is the most common way of getting data from a server. Usually your clients sends a requests to the server, the server interacts with a database and then responds with data to the client.
What we have with rest
- Several round trips to the server to fetch data
- Server responds with a lot of redundant data
- Data is gotten from multiple URLs
What we have with GraphQL
- Declarative data fetching
- Exposes a single endpoint
- GraphQL queries always returns a predictable result
- Gets data in a single request
Project setup
Create a new empty project folder called note-app run
mkdir note-app
Now run npm init
which will initialize the directory wih a package.json
Now we will install the packages we need run
npm i graphql-yoga nodemon
PS: nodemon is a tool that helps us monitor our application for changes and automatically restarts the application
Create a new folder src inside the note-app folder then inside the folder create a file index.js, your file structure should look like this
Open package.json and add
“start”: “nodemon -e js,graphql src/index.js”
This tells nodemon to watch all files ending with .js and .graphql extension and then start the server.
Creating A Graphql Server With GraphQL-Yoga
const { GraphQLServer } = require('graphql-yoga')
const typeDefs = `
type Query {
hello: String!
}
`
const resolvers = {
Query: {
hello: (_, args, context, info) => {
return "Hello World";
},
}
const server = new GraphQLServer({ typeDefs, resolvers })
server.start(() => console.log('Server is running on localhost:4000'))
First we make sure that GraphQLServer is imported from the graphql-yoga package.
typeDefs is a required argument and should be a Schema Definition Language (SDL) string which is used to describe the schema. A GraphQL schema describes the functionality available to the clients which connect to it. Schema Definition Language (SDL) is the syntax for writing GraphQL Schemas. It is otherwise known as Interface Definition Language.
The GraphQL schema provides a clear contract for client-server communication. @nikolasburk
resolvers. A resolver is a function that connects schema fields and types to various backends. Resolvers provide the instructions for turning a GraphQL operation into data. It can retrieve data from or write data to anywhere, including a SQL, No-SQL, or graph database, a micro-service, and a REST API. Resolvers can also return strings, ints, null, and other primitives.
Next we instantiate a GraphQLServer instance and pass our typeDefs and resolvers as an object after which we then call the start() method on the server
now run npm start
you’ll see the output
Server is running on localhost:4000
now goto http://localhost:4000 in your browser, you will see the following result
In the query editor type the following code
query {
hello
}
Next click on the play button to execute the query you will see the following result.
Kicking It Up A Notch
Now that was easy, let’s implement a more sophisticated example.
const { GraphQLServer } = require('graphql-yoga')const posts = [ { id: 1, title: 'Article one', content: 'This is Article One' }, { id: 2, title: 'Article Two', content: 'This is Article Two' }, { id: 3, title: 'Article Three', content: 'This is Article Three' }];const typeDefs = ` type Query { post(postId: Int!): Post allPosts: [Post!]!}type Post { id: Int title: String content: String }`const resolvers = { Query: { post: (_, { postId }, context, info) => { return posts.filter(post => post.id === postId)[0]; }, allPosts: (_, args, context, info) => { return posts; } },}const server = new GraphQLServer({ typeDefs, resolvers });server.start(() => console.log('Server is running on localhost:4000'));
From the code above, we defined a Post type with 3 fields describing what our posts should look like
Next we defined two query actions to fetch a post by id and also to get all the posts from the posts array as we don’t have a database yet
In our resolvers query object, we connect our post and allPosts function to the post and allPosts fields in the Query type.
Using The GraphQL Api
That was easy right, now lets retrieve a post by id. type the following
query {
post(postId: 1){
id
title
content
}
}
Your result should look like this
Notice that, the query requires a parameter (postId) which we pass to the post query and it filters through and return the specific posts.
We also explicitly stated the data we wanted from the api id, title, content
Type the following query in your browser, and click on the execute button
query {
posts{
id
title
content
}
}
You should see the following
GraphQL Mutations
Mutations are like queries, you must define them in your Schema the difference is that you define queries in the Query type and mutations in the Mutation type. Also Mutations let you Create, Update and Mutate data.
Add the code below to your typeDefs, our new mutation has a field addPost which exepects two parameters, title
and content
type Mutation {addPost(title: String!, content: String!): Post}
Add the code below to your resolver
Mutation: {addPost: (_, { title, content }, context, info) => { const newPost = { id: 4, title, content } posts.push(newPost) return newPost; }}
Save the code and our server should automatically restart. In the GraphQL playground, type the following.
mutation{
addPost(title: "Article Four", content: "This is Article Four"){
id
title
content
}
}
Your should now see
Great now we have a fundamental understanding of some GraphQL operations Query
and Mutation
at this point you must be wondering, how then do i interface this with a database? you can make use of literally any database orm like sequelize, mongoose etc. but this is where prisma comes in.
Prisma bridges the gap between your database and GraphQL resolvers. It replaces traditional ORMs and makes it easy to implement resilient, production-ready GraphQL servers. prisma.io
Setting Up Prisma
First ensure to create an account with prisma, then install the prisma cli
npm install -g prisma
now change into the note-app directory and run
prisma init
Select Demo Server + Mysql database, this will require authentication thus the reason why you should create and account.
Next up, choose the region of the server with the lowest latency
The next options are easy to follow,
- choose the name of the service(note-app)
- Name of your stage(dev)
- select the programming language for the generated Prisma client, here make sure to choose Don’t generate
You result should look like this
Now we have two new files in our note-app directory prisma.yml
and datamodel.prisma
prisma.yml is the root configuration file for a Prisma service. Every Prisma service is defined by exactly one prisma.yml. You can think of prisma.yml as a template for one Prisma service.
datamodel.prisma is the foundation for the GraphQL API of your Prisma service. Based on the data model, Prisma will generate a powerful GraphQL schema (called Prisma database schema) which defines CRUD operations for the types in the data model. check out this article
open up prisma.yml and add the following
generate:
- generator: graphql-schema
output: ./src/generated/prisma.graphql
this tells prisma to download the Prisma GraphQL schema and store it in `./src/generated/prisma.graphql`
now open up datamodel.prisma and replace the content with this
type Note { id: ID! @id title: String! content: String!}
This specifies a Note type which will be mapped to a database table with 3 fields id
, title
, and content
now run the following in your console
prisma deploy
Great you’ve now successfully setup Prisma. If you look into ./src/generated/prisma.graphql
file generated by Prisma, you’ll see that Prisma has generated all of the CRUD functionalities we need as well as a tonne of other code, amazing right.
next up we will install prisma-binding
which will enable us query our database for data.
npm i prisma-binding
open up index.js and import Prisma from prima-binding
const { Prisma } = require('prisma-binding')
next up modify you server variable to look like the following
const server = new GraphQLServer({ typeDefs: './src/generated/prisma.graphql', resolvers, context: { db: new Prisma({ typeDefs: './src/generated/prisma.graphql',
endpoint: '__PRISMA_ENDPOINT__' }) }});
the typeDefs specified in our GraphQLServer and Prisma instance, tells it to make use of the generated Prisma GraphQL
ps: the endpoint is specified in your prisma.yml file
context: is an object that gets passed through to the resolver, it enables the resolver to be able to share information. We are going to pass a db object which represents our Prisma instance so we can query data from our database. Think of this instance as a sort of database connector.
Querying Our Database For Data
modify your resolver object to look like the following
const resolvers = {Query: { notes: async (_, args, context, info) => { const notes = await context.db.query.notes(null, info); return notes; } }}
we made use of the db property available on the context object to query our database for all the notes.
now open your browser and type the following query
query{
notes{
id
title
content
}
}
Your result should be the following
there’s no note currently in our database, lets create some notes
Mutation With Prisma: Creating a New Note
Modify your Mutation object to look like the following.
Mutation: { createNote: async (_, args, context, info) => { const note = await context.db.mutation.createNote({ data: { title: args.data.title, content: args.data.content }}, info); return note; }}
now open up your browser and type the following
mutation{
createNote(data: {
title: "First Note",
content: "First Note content"
}) {
title
content
}
}
Your result should be the following
Now that we have a note in our database, let us write a query for a single note. add the following to your Query object
note: async (_, args, context, info) => { const note = await context.db.query.note({ where: { id: args.where.id } }, info); return note;}
In your browser type the following
query{
notes{
id
}
}
this will return all the notes with their ids, in our case there’s only one post
now copy the id and execute the query below
query{
note(where: {
id: "cjzr2xrmrr6cw0b530jsnf3c7"
}) {
id
title
content
}
}
Your result should be the following
Mutations With Prisma: Updating A Note
Add the following code below to your Mutation object
updateNote: async (_, args, context, info) => {const note = await context.db.mutation.updateNote({ where: { id: args.where.id }, data: { title: args.data.title, content: args.data.content } }, info) return note;}
now type this in your GraphQL Playground, this will update the note data with the specified id.
mutation{
updateNote(where: {
id: "cjzr2xrmrr6cw0b530jsnf3c7"
}, data: {
title: "First Title Update",
content: "First Content Update"
}) {
title
content
}
}
your result should be the following
Mutation With Prisma: Deleting A Note
Add the following to your Mutation object
deleteNote: async (_, args, context, info) => {const note = await context.db.mutation.deleteNote({ where: { id: args.where.id } }, info) return note;}
The code above deletes a note with the a specific id.
now type the following in your GraphQL playground
mutation{
deleteNote(where:{
id: "cjzr53vdtsxe30b53s0hazs7w"
}){
id
title
content
}
}
Your result should be the following
Conclusion
In this tutorial you’ve learnt how to setup your own GraphQL server with Node.js and GraphQL-Yoga, you also learnt how to query data from a database using Prisma. We built a simple note taking app, you can kick it up a notch by building other apps leveraging the knowledge you just acquired.
Overall we saw how using GraphQL can easily boost our API development time by leveraging it’s declarative nature and by so doing it makes it easy for our clients to be able to request for just the data they need.
Hope you learnt a lot……Kudos.
Link to the repo
Here are some useful resources for futher learning
References