A GraphQL server can be implemented in various ways. In this tutorial, you’ll learn how to build your own GraphQL server using graphql-yoga from scratch. The final code for this project can be found here.
graphql-yoga is a fully-featured GraphQL server library with focus on simple setup, performance and a great developer experience. It’s the easiest way to build GraphQL servers.

💡 This will be a very practical tutorial. If you’d like to dive deeper in any of the mentioned concepts, we encourage you to check out our article series GraphQL Server Basics - The Structure and Implementation of GraphQL Servers:
When starting out with your GraphQL server with Node & Express, you’re confronted with the choice between a number of libraries. The most popular ones being:
express-graphql is Facebook’s Express middleware released in 2015, alongside the official GraphQL spec and GraphQL.js reference implementation. This was the first library that helped developers to build GraphQL servers.graphql-yoga builds upon a number of other libraries (such as express, apollo-server, graphql-subscriptions and graphql-playground) and creates a great mix of convenience and flexibility with its simple and extensible API.apollo-server-express is the Express version of apollo-server and also built upon Facebook’s GraphQL.js.graphql-yogaIn this tutorial, you’ll build the API for a simple blogging application. You’ll start from scratch and add more functionality to the app step-by-step.
Let’s get started with the tutorial and setup the project!
In a directory of your choice, run the following commands:
mkdir blogr
cd blogr
npm init -y
This creates a new directory called blogr and adds a package.json to it so we can start installing NPM dependencies.
Next, you’ll create the entry-point for the server in a file called index.js.
Inside the blogr directory, run the following commands:
mkdir src
touch src/index.js
Now, go ahead and install the graphql-yoga dependency:
yarn add graphql-yogaAwesome, that’s it! You’re all set, let’s write the first lines of code 🙌
graphql-yoga & Writing your first resolverThe core primitive provided by graphql-yoga is a class called GraphQLServer. It is configured with everything related to the GraphQL schema as well as the web server configuration, such as the port it’s running on or its CORS setup.
For now, you’ll simply instantiate it with a GraphQL schema definition and the corresponding resolver implementation.
Add the following code to src/index.js:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const { GraphQLServer } = require('graphql-yoga')
const typeDefs = `
type Query {
description: String
}
`
const resolvers = {
Query: {
description: () => `This is the API for a simple blogging application`
}
}
const server = new GraphQLServer({
typeDefs,
resolvers
})
server.start(() => console.log(`The server is running on http://localhost:4000`))Here’s is what’s going on in this snippet:
typeDefs contains our GraphQL schema definition written in GraphQL SDL (= Schema Definition Language), it defines the structure of the GraphQL API. The API defined by this schema exposes exactly one query called description which returns a simple string.resolvers is the implementation of the API. Notice how the shape of the resolvers object matches the shape of the schema: Query.description. The implementation is straightforward, all it does at this point is returning a string with a description of the API.typeDefs and resolvers are passed to the constructor of the GraphQLServer. Now, whenever the server receives the description query, it will simply invoke the Query.description resolver and respond to the query with the string returned by that resolver.server. Note that you’re also passing a callback that’s invoked once the server was started —here you simply print a short message to indicate that the server is running on port 4000 (which is the default port of graphql-yoga).So, what happens when you run this thing now?
Go ahead and try it out:
node src/index.jsWell, as expected it prints the message in the console. Go ahead and open http://localhost:4000 inside a browser. What you’ll see is a GraphQL Playground — a powerful GraphQL IDE that lets you interactively work with your GraphQL API. Think of it like Postman, but specifically for GraphQL.
You can now go ahead and send the description query by typing the following into the left editor pane and then hit the Play-button in the middle:
1
2
3
query {
description
}
Here’s is what you’ll see after you sent the query:

Congratulations, you just wrote your first GraphQL server. Easy as pie! 🍰
All right, cool! So, you just learned how to write a GraphQL server with a very basic API. But how does that help for the blogging application we promised you to build? Honest answer: Not too much!
What’s needed is an API that allows to perform certain (domain-specific) operations we'd expect from a blogging app. Let’s lay out a few requirements. The API should allow for the following operations:
Great, that’s four requirements you can directly translate into corresponding API operations.
First, you need to ensure you have a proper data model — in this case, that’ll be represented by a single Post type. Here is what it looks like written in SDL:
1
2
3
4
5
6
type Post {
id: ID!
title: String!
content: String!
published: Boolean!
}
We’ll tell you in a bit where to put that code — bear with us for a minute. Next, you’re going to define the mentioned API operations. That’s done in terms of queries and mutations. Like with the definition of the description query above, you can add fields to the Query type as well as to a new Mutation type in the GraphQL schema definition:
1
2
3
4
5
6
7
8
9
10
type Query {
posts: [Post!]!
post(id: ID!): Post
}
type Mutation {
createDraft(title: String!, content: String): Post
deletePost(id: ID!): Post
publish(id: ID!): Post
}
All right, but where do you put all that SDL code? Well, in theory you could simply add it to the existing typeDefs string in index.js since that’s where you define the API for your GraphQL server. However, a cleaner solution is to define the schema in its own file.
Go ahead and create a new file called schema.graphql in the src directory:
touch src/schema.graphqlschema.graphql contains the definition of your application schema. Here is what it looks like in its entirety:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
type Query {
posts: [Post!]!
post(id: ID!): Post
description: String!
}
type Mutation {
createDraft(title: String!, content: String): Post
deletePost(id: ID!): Post
publish(id: ID!): Post
}
type Post {
id: ID!
title: String!
content: String!
published: Boolean!
}
You simply merged the Post type that was defined as your data model together with the API operations — et voilà — there’s your GraphQL schema definition!
There are few things to note about the types in that example:
Query and Mutation are the so-called [root types](http://graphql.org/learn/schema/#the-query-and-mutation-types of your schema. They define the entry points for the API.ID, String and Boolean are scalar types that are supported by the official GraphQL type system.Post is a custom object type you define inside your schema.! following a type means that the corresponding value can not be null. For example, the posts query can not return a list where some elements would be null. The post query on the other hand might return null if no Post item with the provided id exists. Similarly, all mutations might return null in case they fail.Also, since you’re now defining the application schema in a separate file, you can delete the typeDefs variable from index.js and instantiate the GraphQLServer like so:
const server = new GraphQLServer({
typeDefs: './src/schema.graphql',
resolvers
})
All right, that’s pretty much all you need to know! So, what’s left to do so your API can actually be used? Correct! Implement the corresponding resolvers.
Each root field (i.e. a field on a root type) needs to have a backing resolver so the GraphQL engine inside the GraphQLServer knows what data to return when a query is requesting that field.
For now, you’ll use a simple in-memory store rather than an actual persistence layer. You’re going add a proper database later!
Inside index.js, go ahead and replace the current implementation of the resolvers object with the following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
let idCount = 0
const posts = []
const resolvers = {
Query: {
description: () => `This is the API for a simple blogging application`,
posts: () => posts,
post: (parent, args) => posts.find(post => post.id === args.id),
},
Mutation: {
createDraft: (parent, args) => {
const post = {
id: `post_${idCount++}`,
title: args.title,
content: args.content,
published: false,
}
posts.push(post)
return post
},
deletePost: (parent, args) => {
const postIndex = posts.findIndex(post => post.id === args.id)
if (postIndex > -1) {
const deleted = posts.splice(postIndex, 1)
return deleted[0]
}
return null
},
publish: (parent, args) => {
const postIndex = posts.findIndex(post => post.id === args.id)
posts[postIndex].published = true
return posts[postIndex]
},
},
}
There you go! Each field from the application schema now has a backing resolver:
Query.description: Same as before.Query.posts: Returns our in-memory array called posts where we’re storing all the Post items.Query.post: Searches the posts array for a Post item with a given id. Notice that the id is contained inside the args argument that’s passed into the resolver. If you want to learn more about the various resolver arguments, check out this article.Mutation.createDraft: Creates a new unpublished Post item and appends it to the posts array. Again, the arguments are retrieved from the args object. They correspond to the arguments defined on the belonging root field!Mutation.deletePost: Removes the Post item with the given id from the posts array.Mutation.publish: Sets the published property of the Post item with the given id to true and returns it.Feeling smart yet? 🤓 Go ahead and use this API by starting the server again: node src/index.js.
Here’s a few queries and mutations you can play around with, send them one-by-one in the Playground on http://localhost:4000:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 1. Create a new draft
mutation {
createDraft(
title: "graphql-yoga is awesome"
content: "It really is!"
) {
id
published
}
}
# 2. Publish the draft
mutation {
publish(id: "post_0") {
id
title
content
published
}
}
# 3. Retrieve all posts
query {
posts {
id
title
published
}
}
Here’s what the result of the last query will look like after performing the previous mutations:

Feel free to play around with this API a bit further and explore its capabilities. Of course, no data is actually stored beyond the runtime of the GraphQLServer. When you’re stopping and starting the server, all previous Post items are deleted.
So, with what we’ve covered by now you should have gotten a feeling for what’s going on in the internals of a GraphQL server. You define a schema, implement resolvers and there’s your API!
Note: Resolvers can return data from anywhere — a database, an existing REST API, a 3rd-party service or even another GraphQL API.
The nice thing about resolvers is that they are super flexible, meaning they’re not bound to a particular data source! This allows for example to return data simply from memory as seen in the example, or otherwise fetch data from a database, an existing REST API, a 3rd-party service or even another GraphQL API.
In this example, we’ll connect the resolvers to Prisma. Prisma provides a GraphQL API as an abstraction over a database (in this tutorial, this will be a MySQL DB). Thanks to prisma-binding (hold on a bit, we’ll talk about this soon), implementing the resolvers merely becomes a question of delegating incoming queries to the underlying Prisma API instead of writing complicated SQL yourself.
Think of Prisma as an ORM-like layer for your GraphQL server.
To introduce Prisma into the project, you need to to make a few rearrangements.
First, you need to update the application schema in schema.graphql so that it looks as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
# import Post from "./generated/prisma.graphql"
type Query {
posts: [Post!]!
post(id: ID!): Post
description: String!
}
type Mutation {
createDraft(title: String!, content: String): Post
deletePost(id: ID!): Post
publish(id: ID!): Post
}
Wait what? You’re removing the Post type and instead import it (using a GraphQL comment?!) on top of the file from a file that doesn’t even exist? 😠
Well, yes! The comment syntax for importing stems from the graphql-import library and is not part of the official spec (yet!). You’ll take care of creating the generated/prisma.graphql file in a bit!
Next, you'll setup your Prisma database service.
Once installed, you can use the prisma init command to create a new directory which will contain the configuration files for your Prisma database service:
prisma init databaseWhen prompted by the CLI whether you want to create a new Prisma server or deploy an existing one, select the Demo server and hit Enter.
Note that this requires you to authenticate with Prisma Cloud as that's where the Prisma server is hosted.
Note: If you have Docker installed, you can also deploy to a Prisma server that's running locally. To do so, you can choose the Create new database option in the prompt.
The CLI further prompts you to select a region to which the Prisma service should be deployed as well as a name and a stage for the service. You can just select the suggested values by hitting Enter.
All prisma init is doing here is creating a new directory called database and places two files in there:
prisma.yml: The root configuration file for your Prisma service.datamodel.graphql: Contains the definition of your data model in SDL (Prisma will translate this into an according database schema).Your generated prisma.yml looks similar to this:
1
2
endpoint: https://eu1.prisma.sh/jane-doe/database/dev
datamodel: datamodel.graphqlNote: The
jane-doepart of theendpointwill be different in your case as that's the identifier for your personal workspace in Prisma Cloud. If you have deployed the service locally with Docker,there is no such workspace ID.
Next, update the contents of datamodel.graphql to look as follows:
1
2
3
4
5
6
type Post {
id: ID! @unique
title: String!
content: String!
published: Boolean! @default(value: "false")
}
The definition is identical to the Post from before, except that you’re adding these @default and @unique directives. The @unique directive enforces that no two Post items can have the same value for the id field; the @default directive means that each Post item that will be stored with Prisma will have the value for this field set to false if not otherwise specified. And the best thing is, Prisma is taking care of that without us needing to do anything else. Neat! 🙌
Ok cool! So, what did we win now? So far, not much! But let’s go ahead and deploy the Prisma service and see what we can do then.
Navigate into the database directory and execute prisma deploy:
cd database
prisma deploy
After the command has finished, it prints an HTTP endpoint that you can open in a browser. This is the same endpoint that's already specified in prisma.yml.
When you’re opening the URL with a browser, you’ll see the Playground for the Prisma API. This API defines CRUD operations for the Post type that’s defined in your data model. For example, you can send the following queries and mutations:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 1. Create a new Post
mutation {
createPost(data: {
title: "graphql-yoga is awesome"
content: "It really is!"
}) {
id
published
}
}
# 2. Update title and content of an existing post
mutation {
updatePost(
where: {
id: "__POST_ID__"
}
data: {
title: "New title"
content: "New content"
}
) {
id
title
published
}
}
# 3. Retrieve all posts
query {
posts {
id
title
content
published
}
}
You now have a GraphQL API that mirrors the CRUD operations of the underlying database. But how does that help with your GraphQL server and implementing the resolvers for the application schema?
Meet GraphQL bindings!
GraphQL bindings are a way to easily reuse and share existing GraphQL APIs. Each GraphQL API is represented as a JavaScript object that exposes a number of methods. Each method corresponds to a query or mutation — but instead of having to spell out the entire query or mutation as a string, you can invoke the corresponding method and the binding object will construct and send the query under the hood.
This is particularly nice for typed languages, where you then get compile-time error checks as well as auto-completion features for GraphQL operations! 💯
The first step to introduce bindings is to add the corresponding dependency to your project (the following command needs to be executed inside the blogr, not the database directory):
yarn add prisma-bindingNow, you can update the implementation of index.js as follows:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
const { GraphQLServer } = require('graphql-yoga')
const { Prisma } = require('prisma-binding')
const resolvers = {
Query: {
posts(parent, args, ctx, info) {
return ctx.db.query.posts({ }, info)
},
post(parent, args, ctx, info) {
return ctx.db.query.post({ where: { id: args.id } }, info)
},
},
Mutation: {
createDraft(parent, { title, content }, ctx, info) {
return ctx.db.mutation.createPost(
{
data: {
title,
content,
},
},
info,
)
},
deletePost(parent, { id }, ctx, info) {
return ctx.db.mutation.deletePost({ where: { id } }, info)
},
publish(parent, { id }, ctx, info) {
return ctx.db.mutation.updatePost(
{
where: { id },
data: { published: true },
},
info,
)
},
},
}
const server = new GraphQLServer({
typeDefs: './src/schema.graphql',
resolvers,
context: req => ({
...req,
db: new Prisma({
typeDefs: 'src/generated/prisma.graphql', // the generated Prisma DB schema
endpoint: '__PRISMA_ENDPOINT__', // the endpoint of the Prisma DB service
secret: 'mysecret123', // specified in database/prisma.yml
debug: true, // log all GraphQL queries & mutations
}),
}),
})
server.start(() => console.log('Server is running on http://localhost:4000'))Note that you need to replace the PRISMA_ENDPOINT placeholder in line 47 with the endpoint of your Prisma service (the one specified in prisma.yml).
In each resolver, you’re now basically forwarding the incoming request to the underlying Prisma API with its powerful query engine. Explaining what’s going on there in detail is beyond the scope of this tutorial — but if you want to learn more, check out these articles:
info Argument in GraphQL ResolversIn any case, you can see that the implementation of the resolvers is almost trivial and the hard work is done by the underlying Prisma service. And now just imagine you’d have to write SQL queries in these resolvers… 😱
There is one missing piece before you can start the server again, and that is the dubios generated/prisma.graphql file.
The workflow for getting a hold of this file is based the GraphQL CLI as well as on graphql-config (a configuration standard for GraphQL projects). To get this up-and-running, first need to installed the GraphQL CLI and then create a .graphqlconfig file.
Run the following command in your terminal to install the GraphQL CLI:
npm install -g graphql-cliNext, create you .graphqlconfig file inside the blogr directory:
touch .graphqlconfig.ymlThen, add the following contents to it:
1
2
3
4
5
6
7
8
9
10
projects:
database:
schemaPath: src/generated/prisma.graphql
extensions:
prisma: database/prisma.yml
app:
schemaPath: src/schema.graphql
extensions:
endpoints:
default: http://localhost:4000Notice that you’re also adding information about your local graphql-yoga server — not only the Prisma API!
Now, to generate the prisma.graphql file (also called Prisma database schema), all you need to do is run get-schema command from the GraphQL CLI:
graphql get-schemaBecause the GraphQL CLI “understands” the .graphqlconfig.yml, it knows that it should download the schema from the endpoint specified in prisma.yml and put the generated GraphQL schema definition into src/generated/prisma.graphql, pretty smart huh? 😎
💡 Pro tip: To ensure your Prisma database schema is always in sync with the deployed API, you can also add a post-deploy hook to your prisma.yml file. Whenever you're updating the data model (and therefore the Prisma database schema) by running prisma deploy, the CLI will automatically download the schema for the updated API.
To do so, add the following code to the end of prisma.yml:
1
2
3
hooks:
post-deploy:
- graphql get-schemaAll right, everything is in place now! You can finally start the GraphQL server again: node src/index.js
Because the application schema hasn’t changed (only its implementation was updated), you can send the same queries and mutations from before to test your API. Of course, now the data you're storing will be persisted in the database that's proxied by Prisma.
Also, here’s a little gem: If you download the standalone version of the GraphQL Playground, you can work with the API of your graphql-yoga server and the Prisma API side-by-side (run prisma playground after you downloaded and installed it on your machine). The projects are read from the .graphqlconfig.yml file as well:

In this post, you learned how to build a GraphQL server from scratch. In the end, you had a working API for a blogging application where posts would be stored in an actual database.
Along the way you learned about important concepts, such as GraphQL schemas, resolver functions, GraphQL bindings, graphql-config and a lot more!
Also, we didn’t tell you this before but with this tutorial you basically rebuilt the node-basic GraphQL boilerplate project.
If you want to go one step further and learn about how you can implement authentication and realtime functionality with GraphQL subscriptions, you can check out the fully-fledged Node tutorial on How to GraphQL. To deploy your GraphQL server, check out this tutorial: Deploying GraphQL Servers with Zeit Now.
In case you got lost at some point during the tutorial, you can check out the working version of the final project here.
Namaste 🙏
Was this page helpful?