Building a GraphQL Server using NodeJS and Express
This article is part 3 of the series on Exploring GraphQL. In this article, we are going to build a GraphQL server for food ordering system using NodeJS and Express. This article assumes that you know the fundamental concepts of GraphQL like the use of Query, Mutations, Type System, Alias, Variables, Directives, and Fragments. If you have not heard of these topics before, it is always a good idea to go through the basics and then build something interesting out of it! I recommend reading the first and the second part of the series before heading over to the implementation part.
If you have been following along from the start of the series, you might already be familiar with most of the basic concepts used in GraphQL.
Let’s recap all that we have learned until now in the first and the second part of the series:
- Representational State Transfer (REST) is used for transferring resources across web services. It is being used extensively as a means of data transfer in a variety of applications. But as the complexity of the applications increased, people started experiencing some problems with REST. The REST way of transferring resources to the client is not scalable and has issues such as multiple endpoints, over-fetching, and under-fetching of resources and versioning.
- Facebook built GraphQL as an application layer to power up the data for the news feed section of its IOS application. GraphQL is a strong-typed declarative way of fetching only the required fields from the server. The client has full control over the data with GraphQL. The GraphQL server sends back the response in the exact same shape as requested by the client.
- GraphQL is just a specification that is used to define how clients can communicate with the server using its strict Type System. The client tells the format of the data to the server using queries. The GraphQL Type System supports scalar types, object types, and enums.
- We can pass arguments to the GraphQL queries for the root as well as the nested fields. We can also use Variables for passing dynamic data from the client. Mutations are used for causing side-effects on the server.
Now that we are all good with the GraphQL fundamentals, it’s time to build something. Let’s get to it right away!
Nội Dung Chính
Initializing the project using the Node Package Manager (npm)
Head over to the terminal and type the below command to create an empty node project.
npm init
It would ask you a few straightforward questions. If you want to keep the default values, keep pressing enter. This will create a package.json
file inside your project directory. The package.json
file is used for managing the project dependencies.
Let’s install a few node modules
npm install express graphql express-graphql nodemon --save
express
– It is a Node.js web application framework that is used for building web and mobile applications
graphql
– It provides the type schema and serves queries against that schema
express-graphql
– It is a middleware for creating GraphQL HTTP server
nodemon
– It automatically restarts the server when it detects a change in the application
Express Server
Create a server.js
file inside the root directory of the project.
const express = require('express')
const expressGraphQL = require('express-graphql')
const schema = require('./schema')
const app = express()
app.use('/graphql', expressGraphQL({
schema,
graphiql: true
}))
app.listen(3000, () => {
console.log('Server is running at port 3000')
})
const app = express()
is used for creating an express application. app.use
is used for binding the application-level and router-level middlewares. The middleware functions have access to the request object, response object and the next middleware function in application’s request-response cycle.
The application-level middleware app.use
will be executed every time the application receives a request.
In this case, we’re passing the endpoint /graphql
to the middleware. If the endpoint of the request object matches with /graphql
, its callback will be executed. The callback runs the expressGraphQL
function that takes in the schema
and the boolean
value for graphiql
.
We will look at the schema file in a moment. graphiql
is an in-browser IDE and value of true
would integrate the application with graphiql
. The last line in the above code sets up the server on port 3000.
GraphQL Schema
Let’s create a schema.js
file at the root level of the project. We will first create a simple schema that has only one field called data
.
As we have seen in the previous articles, the type system of GraphQL supports scalar and object types. Let’s import these types in the schema
file like:
const graphql = require('graphql')
const {
GraphQLString,
GraphQLInt,
GraphQLObjectType,
GraphQLSchema
} = graphql
The types GraphQLString
and GraphQLInt
are the scalar types for defining the String
and Number
values, GraphQLObjectType
is used for defining objects and GraphQLSchema
is a type for Query
and Mutation
.
RootQuery
Let’s write our first RootQuery
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
data: {
type: GraphQLString,
resolve (parentValue, args) {
return 'Hello, Graphql!'
}
}
}
})
The RootQuery
is of type GraphQLObjectType
and with name RootQueryType
. The fields
object is used for defining all the fields in a particular type. The RootQueryType
has just one field called data
, which is of type GraphQLString
. The data
field resolves to a string Hello, GraphQL!
The last step in building our Hello, GraphQL
server is to export our schema. Let’s do that!
module.exports = new GraphQLSchema({
query: RootQuery
})
We have wrapped the RootQuery
inside the GraphQLSchema
instance. This is it! We have set up our server.
Let’s do a few changes in package.json
file to have our server up and running.
Setting-up nodemon
To setup nodemon
, write a start
script as below:
"scripts": {
"start": "nodemon server.js"
}
nodemon
listens for changes in server.js
and accordingly restarts the application.
npm start
You can access your application in the browser on http://localhost:3000/graphql
. Please note the endpoint
is /graphql
here.
GraphQL server for Food Ordering System
Use-Case: A user should be able to browse a list of restaurants. He can see the name, location, contact, email address and the menu for these restaurants. He can place an order for any item from the menu of his favorite restaurant. The customers’ order should be mapped accordingly with the restaurants.
Implementation Plan
We will have the following fields in the RootQuery
restaurants
– displays a list of restaurants. This will be an array of objects and each object will have id
, name
, location
, email
and the menu
fields.
restaurant
– displays the details of a particular restaurant. It accepts the restaurant id as the input.
users
– displays the list of users in the system. This will be an array of objects and each object will have id
, name
, contact
and location
orders
– displays the list of orders placed by users for their selected restaurants
If you get lost anywhere in the code below, you can refer my GitHub repository to get to speed. Have fun building Graphql server!
GraphQL is an application layer; it is not concerned about the type of data store. The data store can be MySQL, MongoDB or even a JSON file.
I already have static data in place so we can focus on the implementation part right away! Please copy the data.json
file from here. (Disclaimer: It is a work of fiction and that businesses, locations, and organizations while real are used in a way that is purely fictional)
We will be using json-server
to fetch data from the dump (data.json file
) and axios
as the HTTP client.
Let’s install the dependencies for these two modules:
npm install axios json-server --save
We will have to write a script in the package.json
file to run the json-server
.
Now the scripts
object in package.json
will look like:
"scripts": {
"start": "nodemon server.js",
"json-server": "json-server data.json --watch --port 4200"
}
Let’s start the json-server
at port 4200 using the below command:
npm run json-server
We will have to modify the RootQuery
to include the required fields – restaurants
, restaurant
, users
, orders
.
Let’s first make room for the restaurants
field:
const graphql = require('graphql')
const axios = require('axios')
const {
GraphQLString,
GraphQLInt,
GraphQLObjectType,
GraphQLSchema,
GraphQLList
} = graphql
const MenuItem = new GraphQLObjectType({
name: 'MenuItemType',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
price: { type: GraphQLString },
})
})
const Restaurant = new GraphQLObjectType({
name: 'RestaurantType',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
email: { type: GraphQLString },
location: { type: GraphQLString },
menu: { type: GraphQLList(MenuItem) }
})
})
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
restaurants: {
type: GraphQLList(Restaurant),
resolve (parentValue, args) {
return axios.get(`http://localhost:4200/restaurants`)
.then(res => res.data)
}
}
}
})
module.exports = new GraphQLSchema({
query: RootQuery
})
This is the updated schema.js
file. The RootQuery
has one field restaurants
, which is a GraphQLList
of type Restaurant
. The resolve
function of this field returns the restaurants data by making an axios
request to the json-server
. Please note: the resolve
function takes in two arguments parentValue
and args
.
The type Restaurant
has fields – id
, name
, email
, location
, and menu
is a GraphQLList of type MenuItem
. The MenuItem
type has fields id
, name
and price
of type GraphQLString
. We have specified the exact fields for the types and this makes GraphQL a strong-typed system.
Let’s query restaurants
using graphiql:
{
restaurants {
id
name
email
location
menu {
name
price
}
}
}
The above query renders the response in the exact same shape.
{
"data": {
"restaurants": [
{
"id": "res_1",
"name": "Daniel",
"email": "[email protected]",
"location": "Boston",
"menu": [
{
"name": "Veg Sandwich",
"price": "$25"
},
{
"name": "Cheese Pizza",
"price": "$30"
}
]
},
{
"id": "res_2",
"name": "Lincoln Square Steak",
"email": "[email protected]",
"location": "California",
"menu": [
{
"name": "Thai Basil Salad",
"price": "$30"
},
{
"name": "Grilled Spring Onions",
"price": "$40"
},
{
"name": "Dutch Choclate Cake",
"price": "$150"
}
]
},
{
"id": "res_3",
"name": "The Little Owl",
"email": "[email protected]",
"location": "Maryland",
"menu": [
{
"name": "Poached Eggs",
"price": "$25"
},
{
"name": "Breakfast Wrap",
"price": "$20"
},
{
"name": "Pancakes",
"price": "$20"
}
]
}
]
}
}
Check out the documentation of the right-hand side that clearly lists down the types for various fields used in the Restaurant
.
As we can see from the above image, graphiql works as expected!
Let’s add the restaurant
field for fetching details of a particular restaurant:
fields: {
// ...restaurants field,
restaurant: {
type: Restaurant,
args: {
id: { type: GraphQLString }
},
resolve (parentValue, args) {
return axios.get(`http://localhost:4200/restaurants/${args.id}`)
.then(res => res.data)
}
}
}
As we can see from the above code, the restaurant
field returns an object of type Restaurant
. args
is used for specifying the input arguments. In our case, restaurant
accepts an input argument id
of type GraphQLString
. The resolve
function returns data for a particular restaurant from the JSON dump.
Hit the below query in the graphiql and check out its response
{
restaurant (id: "res_2") {
id
name
email
location
menu {
name
price
}
}
}
Our restaurants part is up and running! The users can browse the list of restaurants and can also see the details of a particular restaurant.
Let’s implement the Customer
and the Orders
type now.
Here’s how your schema.js
file should look after adding all the types:
const graphql = require('graphql')
const axios = require('axios')
const {
GraphQLString,
GraphQLInt,
GraphQLObjectType,
GraphQLSchema,
GraphQLList
} = graphql
const MenuItem = new GraphQLObjectType({
name: 'MenuItemType',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
price: { type: GraphQLString },
})
})
const Restaurant = new GraphQLObjectType({
name: 'RestaurantType',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
email: { type: GraphQLString },
location: { type: GraphQLString },
menu: { type: GraphQLList(MenuItem) }
})
})
const Customer = new GraphQLObjectType({
name: 'UserType',
fields: () => ({
id: { type: GraphQLString },
name: { type: GraphQLString },
email: { type: GraphQLString },
location: { type: GraphQLString }
})
})
const Order = new GraphQLObjectType({
name: 'OrderType',
fields: () => ({
id: { type: GraphQLString },
customerId: { type: GraphQLString },
restaurantId: { type: GraphQLString },
order: { type: GraphQLList(GraphQLString) }
})
})
const RootQuery = new GraphQLObjectType({
name: 'RootQueryType',
fields: {
restaurants: {
type: GraphQLList(Restaurant),
resolve (parentValue, args) {
return axios.get(`http://localhost:4200/restaurants`)
.then(res => res.data)
}
},
restaurant: {
type: Restaurant,
args: {
id: { type: GraphQLString }
},
resolve (parentValue, args) {
return axios.get(`http://localhost:4200/restaurants/${args.id}`)
.then(res => res.data)
}
},
customers: {
type: GraphQLList(Customer),
resolve (parentValue, args) {
return axios.get(`http://localhost:4200/customers`)
.then(res => res.data)
}
},
customer: {
type: Customer,
args: {
id: { type: GraphQLString }
},
resolve (parentValue, args) {
return axios.get(`http://localhost:4200/customers/${args.id}`)
.then(res => res.data)
}
},
orders: {
type: GraphQLList(Order),
resolve (parentValue, args) {
return axios.get(`http://localhost:4200/orders`)
.then(res => res.data)
}
},
}
})
module.exports = new GraphQLSchema({
query: RootQuery
})
Here’s a screenshot of the documentation section after adding these types:
Please feel free to clone my GitHub repository to experiment more with the Graphql server.
Conclusion
In this tutorial, we learned how to build a GraphQL server using Express. The GraphQLSchema
takes in an object as the input parameter. This object has one of the fields as query
, which is used to define the RootQuery
for the schema. Each of the types has a name, type, and fields section. The resolve
function is used to return the corresponding data for a particular field.
In the next tutorial, we will learn how to place orders and modify the JSON dump using Mutation
. We will also learn how to set up pagination on the server to send only the required count of restaurants, customers and orders to the front-end.
Next in series: How to implement Pagination and Mutation in GraphQL