How to set up a GraphQL server: A Beginner’s Guide to GraphQL

Daminda Dinesh Imaduwa Gamage
10 min readMay 24, 2023

Introduction to GraphQL

GraphQL is a query language that makes it easy for software developers to request data from a server. It was developed by Facebook and is now used by many large companies around the world.

In simple terms, GraphQL allows developers to specify exactly what data they need, and the server responds with only that data. This means that developers can create more efficient, faster, and more flexible applications, without worrying about over-fetching or under-fetching data.

With GraphQL, developers can easily retrieve and manipulate data from various sources, such as databases, microservices, and APIs, using a single query language. This makes it easier for developers to work with complex systems and build powerful applications.

Why GraphQL

GraphQL was invented as a solution to tackle the drawbacks of REST API. Therefore, let’s see what advantage you have using GraphQL compared to REST API

Advantages ( compared to REST API )

Disadvantages

Now, let us see if there is any disadvantage to using GraphQL in your next application.

What do you need to start a project with GraphQL?

Before we start any applications with GraphQL, let us see what technologies and skills you need to know.

  1. A server

You will need a server to serve your GraphQL API. You can use any web server technology you like, such as Node.js, Python, Ruby,Java,.NET

2. A database

You will need a database to store your data. GraphQL is database agnostic, which means it can work with any database technology you like, such as MongoDB, MySQL, PostgreSQL, or Redis.

3. A GraphQL implementation:

  • You will need a GraphQL implementation library for your chosen server technology.
  • There are many popular GraphQL libraries available, such as Apollo Server, Express GraphQL, and GraphQL Yoga.

4. A GraphQL schema:

  • You will need to define a GraphQL schema that describes your data model and operations.
  • This schema should be written in the GraphQL Schema Definition Language (SDL), which is a simple syntax for defining your API’s types and operations.

5. A client:

  • You will need a client application to consume your GraphQL API.
  • This could be a web application, a mobile application, or any other type of application that can make HTTP requests.
  • There are many GraphQL client libraries available, such as Apollo Client, Relay, and urql.

6. Knowledge of GraphQL:

To work with GraphQL, you will need to have a good understanding of how it works and how to write GraphQL queries, mutations, and subscriptions with GraphQL query language

GraphQL Query language

GraphQL Query Language is a powerful and flexible language used to retrieve data from a GraphQL API/ GrapQL server. It allows you to specify the exact data you need by selecting fields on the available types in the schema. With GraphQL Query Language, you can define complex queries with nested fields, pass arguments to filter and sort data, use aliases to rename fields, leverage fragments for reusable query snippets, and employ directives to conditionally include or skip fields. Additionally, variables enable dynamic values in queries, making them adaptable to various scenarios.

Here’s an example of a GraphQL query that retrieves information about a user’s name, email, and the title of their latest 3 posts:

query {
user(id: "123") {
name
email
posts(last: 3) {
edges {
node {
title
}
}
}
}
}

This query specifies the fields you want to retrieve for the specified user and their posts, and the server will only return those fields, instead of returning the entire user and posts objects.

Learn more about how to write GraphQL queries in my post: GraphQL Query: How to Talk to the GraphQL Server.

GraphQL project setup

In a typical GraphQL project, the front-end and back-end are separated into distinct layers, with the GraphQL layer acting as a mediator between them.

The frontend can be any application that sends HTTP requests and renders responses on a UI. The GraphQL layer( GraphQL server ) sits in between the frontend and backend layers and serves as a bridge between them. It contains schema definitions and resolver functions. While schema definition describes the data available in the API, resolver functions can handle incoming queries, mutations, and subscriptions and delegate them to the appropriate backend service or data source. In return, the backend API fetches data from the data source and responds to the GrapQL server via resolver functions.

An alternative setup involves integrating the GraphQL server directly into the backend of the system. This setup has the advantage of having efficient communication between the server and backend data sources. However, it also has a downside in terms of scalability. By contrast, when the GraphQL server is implemented as middleware, it allows for greater scalability of the system and more flexibility in choosing and changing backend data sources without affecting the front-end API.

The GraphQL Server: know these concepts before setting up

A GraphQL server is a software application that implements the GraphQL specification and provides an API for clients to interact with. It receives and processes GraphQL queries from clients and returns the requested data in a JSON format.

The server is responsible for resolving the queries and mutations by invoking the appropriate resolver functions. These functions retrieve data from the backend data sources and transform it into the format expected by the GraphQL schema.

The server also handles subscriptions, which allow clients to receive real-time updates as the data changes. The GraphQL server can be implemented in any programming language and can integrate with a wide variety of data sources and middleware.

What is a schema in GraphQL?

The building block of a GraphQL server is the schema. A schema is a collection of Types that define the data available in an API, the relationships between them, and the operations that can be performed on them

Types define the shape of the data that can be queried or mutated.

A Type (GraphQL type or building blocks of the schema ) contains one or more fields that represent specific pieces of data and their types( primitive data types/scalar types), and it can also include other types as fields to represent relationships between data.

A schema is written in SDL( Schema Definition Langauge ), which is a human-readable syntax that describes the structure of your data and the way your API clients can access it.

type Author {
id: ID!
name: String! # returns a String and non-nullable
books: [Book!]!# A list of Books
}
type Book {
id: ID!
title: String!
author: Author! # returns an Author
}

In the above schema, there are two types( GraphQL types) of data, Author, and Book. An Author has an id, a name, and a list of books that they have written. A Book has an id, a title, and an author who wrote the book.

Note the ! after the type of the “id” field in both types. The exclamation mark indicates that the field is non-nullable, meaning it is required and cannot be null.

In addition to the type definitions, you can also define queries, mutations, and subscriptions in a schema.

Learn more about defining schemas and resolvers for queries and mutation in this post.

What are Resolver functions?

Resolver functions are responsible for fetching the data for a single field in your schema. These functions are executed based on queries, mutations, or subscriptions defined in the schema.

Therefore, while this is a common use case for resolvers for fetching data from the backend, they can also be used to perform other actions, such as writing data to a backend data source, invoking external APIs, and performing computations on data before returning it to the client.

You can pass four arguments to a resolver function. All of them are optional.

  1. parent: The result of the previous resolver call, which is the parent field value.
  2. args: The arguments provided for the field in the GraphQL query.
  3. context: A value that is provided to every resolver and holds important information such as the currently logged-in user or database connection.
  4. info: Information about the execution state of the GraphQL operation, including the field name, field AST, and schema.

let’s take a look a the following schema definition.

type Query {
book(id: ID!): Book
author(id: ID!): Author
}

type Book {
id: ID!
title: String!
author: Author!
}

type Author {
id: ID!
name: String!
books: [Book]!
}

for the purpose of this example, we use the following array of objects as our data source

const authors = [
{ id: "1", name: "J.K. Rowling" },
{ id: "2", name: "Stephen King" },
{ id: "3", name: "Haruki Murakami" },
];

const books = [
{ id: "1", title: "Harry Potter and the Philosopher's Stone", authorId: "1" },
{ id: "2", title: "The Shining", authorId: "2" },
{ id: "3", title: "1Q84", authorId: "3" },
];

Thus, the resolver functions would be

// Resolvers define how to fetch the types defined in your schema.
// This resolver retrieves books from the "books" array above.
const resolvers = {
// Define resolvers for queries
Query: {
// Return all books
books: () => books,
// Find a book by ID and return it
book: (parent, args) => books.find(book => book.id === args.id),
// Return all authors
authors: () => authors,
// Find an author by ID and return them
author: (parent, args) => authors.find(author => author.id === args.id)
},
// Define resolvers for Author type fields
Author: {
// Return all books written by the author
books: (parent) => books.filter(book => book.authorId === parent.id)
},
// Define resolvers for Book type fields
Book: {
// Return the author of the book
author: (parent) => authors.find(author => author.id === parent.authorId)
}
};

In the above code, there are five resolver functions.

  1. books : return all books
  2. book : find a book by ID and return it
  3. authors : return all authors
  4. author : find an author by ID and return them
  5. author (nested in Book type): return the author of the book

Note: These resolvers are for query type. If your schema has mutations or subscriptions, you can have resolvers for them too.

Finally, let’s set up the Apollo GraphQL server

  1. mkdir graphql_project //Create a project folder
  2. mkdir graphql_project/server //create another folder inside graphql_project. Name it as server
  3. cd graphql_project/server //move into the "server" folder
  4. npm init //initialize the node project
  5. npm install @apollo/server graphql //install necessary packages
  • graphql : The library that implements the core GraphQL parsing and execution algorithms.
  • @apollo/server : The Apollo GraphQL server

6. Now, open your project folder in a text editor

7. We will be using ES Modules in setting up the project. so, it would help if you modified the package.json file.

  • add "type":"module" to package.json file

8. Create an index.js file in the server folder, and add the “import” statements

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

9. Add the following schema to index.js file

import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';

// A schema is a collection of type definitions (hence "typeDefs")
// that together define the "shape" of queries that are executed against
// your data.

const typeDefs = `
type Author {
id: ID!
name: String! # returns a String and non-nullable
books: [Book!]!# A list of Books
}

type Book {
id: ID!
title: String!
author: Author! # returns an Author
}

type Query {
authors: [Author!]!
author(id: ID!): Author!
books: [Book!]!
book(id: ID!): Book!
}
`

10. Now, add the sample data set to index.js. please note this is for the purpose of this example. Apollo server can fetch data from other data sources such as databases, REST API, or JSON files.

//sample dataset. We will fetch data from these array of objects
const authors = [
{ id: "1", name: "J.K. Rowling" },
{ id: "2", name: "Stephen King" },
{ id: "3", name: "Haruki Murakami" },
];

const books = [
{ id: "1", title: "Harry Potter and the Philosopher's Stone", authorId: "1" },
{ id: "2", title: "The Shining", authorId: "2" },
{ id: "3", title: "1Q84", authorId: "3" },
];

11. Add the following resolver functions to index.js

// Resolvers define how to fetch the types defined in your schema.
const resolvers = {
// Define resolvers for queries
Query: {
// Return all books
books: () => books,
// Find a book by ID and return it
book: (parent, args) => books.find(book => book.id === args.id),
// Return all authors
authors: () => authors,
// Find an author by ID and return them
author: (parent, args) => authors.find(author => author.id === args.id)
},
// Define resolvers for Author type fields
Author: {
// Return all books written by the author
books: (parent) => books.filter(book => book.authorId === parent.id)
},
// Define resolvers for Book type fields
Book: {
// Return the author of the book
author: (parent) => authors.find(author => author.id === parent.authorId)
}
};

12. Last step is to configure the Apollo server and start it.

// The ApolloServer constructor takes two parameters: the schema and the resolvers you created 
const server = new ApolloServer({
typeDefs,
resolvers,
});

// Passing an ApolloServer instance to the `startStandaloneServer` function.
// start the server at port 4000 on localhost
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
});

console.log(`Server starts at: ${url}`);

12. start your server at http://localhost:4000, and run some test queries. In this video, I am using the GraphQL query language to fetch data from the GraphQL server.

Last thoughts

In this introduction to GraphQL, we covered the reasons why GraphQL is useful and its advantages over traditional REST APIs. We also discussed the steps required to set up a GraphQL project, including the important concepts of schema and resolvers. Before delving into more advanced GraphQL topics, it’s essential to understand how to create a GraphQL server, which we explained in detail. Now that you have the knowledge to run a GraphQL server on your local machine, you can start practicing by writing simple queries and becoming familiar with the GraphQL syntax in SDL.

You can learn how to define schemas for queries and mutations in my next GraphQL post in detail.

Resources

Introduction to GraphQL

Introduction to Apollo Server

--

--