Play it your way: POC on GraphQL

Rahul Pawar
Globant
Published in
4 min readJan 8, 2021

GraphQL+Express+PostgreSQL+MongoDB

GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data. GraphQL isn’t tied to any specific database or storage engine and is instead backed by your existing code and data.

Until now, we have seen what is GraphQL in the article Introduction: Overview of GraphQL and how to use GraphQL query language to query data from GraphQL runtime using GraphiQL user interface in the article GraphQL: The Query Language.

In this session, we will set up the GraphQL runtime using GraphQL+Express+PostgreSQL+MongoDB

To set up a GraphQL server utilizing two different database connections namely PostgreSQL and MongoDB, we will be creating a NodeJs server using the Express framework.

Let’s start by installing dependencies.

Install Dependencies:

npm install express express-graphql graphql mongodb pg

express: NodeJs Framework

express-graphql: GraphQL module for integration with Express

graphql: GraphQL lib

mongodb: MongoDB node module

pg: PostgreSQL node module

Setup GraphQL-Express Server

Basic Express server setup is all we need:

// ./index.js
const app = require('express')();
const Schema = require('./schema');
const graphqlHTTP = require('express-graphql');
const PORT = process.env.PORT || 3000;
app.use('/graphql', graphqlHTTP({ schema: Schema, graphiql: true}));app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});

You might be wondering about the Schema here, let’s find out what it is next.

GraphQL Schema

There are basically two types of APIs in GraphQL

  • query: These are data fetching API’s.
  • mutation: These are the ones that will have side effects i.e. modify data.

The GraphQL object which defines and differentiates between these two API’s is GraphQLSchema

// ./schema/index.js
const { GraphQLSchema } = require('graphql');
const RootQueryType = require('./rootquerytype');
const RootMutationType= require('./rootmutationtype');
module.exports = new GraphQLSchema({
query: RootQueryType,
mutation: RootMutationType
});

To define the query and mutation APIs, GraphQL provides a helper class GraphQLObjectType

GraphQLObjectType

The GraphQLObjectType details the query and mutation API definitions.

new GraphQLObjectType({
name: 'TypeName',
description: 'Type Description',
fields: {
hello: {
type: GraphQLString,
description: 'API description',
resolve: () => 'world'
}
}
});

The name, descriptionexposes the documentation details in GraphiQL interface and fields key holds all the query or mutation API path keys. This, in turn, defines the resolver function which will have code either to return query or mutate the data in the database. The type defines the return type of the API.

Before we write a query or mutation query let’s create database connections.

Database Connections

We are going to demonstrate a GraphQL server with multiple database connections ie. PostgreSQL and MongoDB.

Here we are going to use PostgreSQL for user data and MongoDB to contain the users count.

Create on database and table in PostgreSQL.

create database graphql_test;
# connect to the graphql_test database
create table users(
id integer generated by default as identity,
first_name character varying
);
insert into users(first_name) values('test_user_1'); #dummy data

MongoDB will create the database on the fly.

Let’s update the GraphQL-Express to create and consume the PostgreSQL and MongoDB connections.

Here we are considering both the databases are running on your machine and on their respective default ports.

// ./index.js
const app = require('express')();
const pg = require('pg');
const { MongoClient } = require('mongodb');
const Schema = require('./schema');
const graphqlHTTP = require('express-graphql');
const PORT = process.env.PORT || 3000;
const pgPool = new pg.Pool({
user: 'postgres',
host: 'localhost',
database: 'graphql_test',
password: 'xxxxx'
});
MongoClient.connect('mongodb://localhost:27017/graphqltest',
(err, mongoClient) => {
app.use('/graphql', graphqlHTTP({
schema: Schema,
graphiql: true,
context: {
pgPool : pgPool,
mongo : mongoClient.db("graphql-training")
}
})
);
app.listen(PORT, () => {
console.log(`Server is listening on port ${PORT}`);
});
});

We have passed the database connections to the context option which provides a way to share objects throughout the application resolver function. Now in query and mutation API resolver functions, we will consume those connections.

GraphQL Query API

// ./schema/rootquerytype.js
const { GraphQLString, GraphQLInt, GraphQLList, GraphQLObjectType } = require('graphql');
const RootQueryType = new GraphQLObjectType({
name: 'RootQueryType',
description: 'This holds all the query APIs',
fields: {
users: {
type: new GraphQLList(UserType),
description: 'Handler for get users list',
resolve: async ( obj, args, context) => {
const { pgPool } = context;
pgPool.query(`select * from users`, [])
.then(res => res.rows);
}
},
usersCount: {
type: GraphQLInt,
description: 'Handler for get usersCount',
resolve: async ( obj, args, context) => {
const { mongo } = context;
return mongo.collection('metrics').findOne({key :
'userCount'}).then(res => res.value);
}
}
}
});
module.exports = RootQueryType;
// ./schema/types/UserType.jsconst UserType = new GraphQLObjectType({
name: 'UserType',
description: 'User object type',
fields:{
id: {
type : GraphQLInt,
resolve: (obj)=> obj.id
},
firstName:{
type: GraphQLString,
resolve: (obj)=> obj.first_name
}
}
})
module.exports = UserType;

The resolver function accepts three arguments:

  • obj: base object reference.
  • args: Arguments passed for the query path.
  • context: Context object passed down from the index.js file which holds the database connection objects.

The users field in RootQueryType holds the object with keys:

  • type: return type of the resolver function.
  • resolve: resolver function to fetch the users list.

GraphQL Mutation API

// ./schema/rootmutaiontype.js
const { GraphQLString, GraphQLObjectType } = require('graphql');
const RootMutationType = new GraphQLObjectType({
name: 'RootMutationType',
description: 'this holds all the mutation APIs',
fields: {
user: {
type: UserType,
args: { input:{type : UserArg}},
description: 'Handler for create user',
resolve: async ( obj, {input}, context) => {
const { pgPool, mongo } = context;
const user= await pgPool.query(`insert into users(fist_name)
values($1) returning *`,[input.firstname])
await mongo.collection('metrics')
.update({ key : 'userCount' },{ $inc: { value: 1 }},{
upsert: 1 });
return user
}
}
}
});
module.exports = RootMutationType;
//./schema/types/UserArg.jsconst { GraphQLString, GraphQLInputObjectType } = require("graphql");let UserArg = new GraphQLInputObjectType({
name: 'UserArg',
fields:{
firstname: { type : GraphQLString }
}
})
module.exports = UserArg;

Same as the Query API, we need to implement a resolver function to add new users and update the count.

For accepting input arguments, we have defined input of type UserArg which is of GraphQLInputObjectType which accepts firstname of GraphQLString type.

Conclusion

And that’s it! Now you know how to set up the GraphQL server with multiple connections and consume it.

Source code: https://github.com/rahulpawarglobant/graphql-training

--

--