In this blog post we will start using graphql using expressjs.
I have been working with REST API’s for a very long time so, these blog posts would be learning graphql but in context of rest api as well.
To start of, do the following https://graphql.github.io/graphql-js/
https://graphql.github.io/graphql-js/running-an-express-graphql-server/
After the first step is done, you should have a basic gql application running
Next after this is done, do this step https://graphql.github.io/graphql-js/basic-types/
This will give you a basic idea of “queries” in graphql.
Now let’s see step by step, how we can go about learning this in details. We will focus on different examples of queries so that we can get a good hands on it.
Step1
Let’s make a simple query first. Query name “hello” which will return a string “hello world”
This is now the code looks for it
var express = require('express');
var graphqlHTTP = require('express-graphql');
var { buildSchema } = require('graphql');
// Construct a schema, using GraphQL schema language
var schema = buildSchema(`
type Query {
hello : String
}
`);
var root = {
hello: () => {
return "hello world";
}
};
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: root,
graphiql: true,
}));
app.listen(4000);
console.log('Running a GraphQL API server at localhost:4000/graphql');
https://github.com/nodeexcel/gql_tutorial/commit/5817283ef59e58d64ca2365d045f8385e7b98e45
Next run the app from terminal via “node index.js”
and then open browser and run the query “hello” there as shown in screenshot
This is our first very simple query, but lets first understand what exactly happened here (in context of rest api)
- We created a “query” called “hello” this is similar to a rest api endpoint or route in express.
- We created a resolver which is similar to the response function of an end point
- There is no concept of GET, POST, PUT etc in terms on GQL.
- There is no concept of an api url, its simply the GQL query we are firing which defines it.
- The response of query is well typed i.e we know its of type String
In short, “query” is similar to a rest api end point. Cool!
Step2
Graphql schema language!
In many applications i saw usage of graphql build schema library, so let’s use it at this stage to so we can understand it as well.
P.S. Right now when every time you make any change to your index.js you need to reload your terminal. Instead install nodemon via command sudo npm -g install nodemon and then do nodemon index.js to run. This will automatically restart when you make changes to code.
Now, our same query looks like this
var queryType = new graphql.GraphQLObjectType({
name: 'Query',
fields: {
hello: {
type: graphql.GraphQLString,
resolve: (_) => {
return "test"
}
}
}
});
var schema = new graphql.GraphQLSchema({ query: queryType });
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
graphiql: true,
}));
https://github.com/nodeexcel/gql_tutorial/commit/6ef59269dc028e7c9712a9f45961964cb7baae27
Step3
Let’s create another query of type Int e.g query “random” and response is a any random no between 1, 100
Code looks like this
var queryType = new graphql.GraphQLObjectType({
name: 'Query',
fields: {
hello: {
type: graphql.GraphQLString,
resolve: (_) => {
return "test"
}
},
number: {
type: graphql.GraphQLInt,
resolve: () => {
return Math.round(Math.random() * 100 , 0)
}
}
}
});
https://github.com/nodeexcel/gql_tutorial/commit/1c63e6e383e0d3729f10578d01c4e76cfc6e8bef
Cool, we created a new query. Things to note
- We have 2 queries, similar to 2 rest api endpoints
- Both our responses are strongly typed, if any of our resolver’s don’t return the correct type it will give error. You can check by removing Math.round from the random function
- We can call both queries together or even separately. This is a very powerful feature, and we will see its importance later.
Step4
Let’s create a more complex type of query. A more real world type of situation
Suppose we can make a profile/ rest api which returns a user object. Let’s see how we would go about it.
var profileType = new graphql.GraphQLObjectType({
name: 'Profile',
fields: {
id: {
type: graphql.GraphQLID
},
name: {
type: graphql.GraphQLString
}
}
})
var queryType = new graphql.GraphQLObjectType({
name: 'Query',
fields: {
hello: {
type: graphql.GraphQLString,
resolve: (_) => {
return "test"
}
},
number: {
type: graphql.GraphQLInt,
resolve: () => {
return Math.round(Math.random() * 100, 0)
}
},
profile: {
type: profileType,
resolve: () => {
return {
id: "1",
name: "Manish"
}
}
}
}
});
Ok, there few new things here.
- We create a new type, “profileType” and defined it same as a Query.
- We used a new Graphql scalar type called “GraphQLID”. We could have used normal integer as well, but when you are defining an ID better use this. https://stackoverflow.com/a/39472164
- What is interesting to note, we are able to define a tree structure of “queries”. This is again a very powerful feature, we will see this further in more detail
Now if you try to run this query like this
You will get an error, the reason is https://stackoverflow.com/a/46118737
In short, all our queries to resolve to a scalar value.
Hence our query should be like
Now it works
Step5
Let’s assume for every user we have address associated with profile. So for every user will return an address as well. Let’s see how to do it
var addressType = new graphql.GraphQLObjectType({
name: "address",
fields: {
id: { type: graphql.GraphQLID },
address: { type: graphql.GraphQLString },
country: { type: graphql.GraphQLString },
phone: { type: graphql.GraphQLString }
}
})
var profileType = new graphql.GraphQLObjectType({
name: 'Profile',
fields: {
id: {
type: graphql.GraphQLID
},
name: {
type: graphql.GraphQLString
},
address: {
type: addressType,
resolve: () => {
return {
id: "1",
address: "Noida",
country: "IN",
phone: "99999"
}
}
}
}
})
So, now we have extended our profileType and added addressType to it as well.
And when we run our “query” it looks like this
Let’ see in this more details in comparison to rest api
- We are able to define exactly what we require in our request. This is a very unique feature of GQL. In rest api, we cannot control the response at all, its always a fixed response. e.g if don’t want address data, simply don’t request it from the json.
- Typically in a rest api structure we would have to write 2 api’s one for profile and one for address, but here we are just writing one. This is a very effective as it reduced the front end requests.
- All our responses are strongly typed.
- Our request, response are in json format, this is what all modern applications required these days. A tree structure of json.
https://github.com/nodeexcel/gql_tutorial/commit/a03faedebe58ce19d00bfe660c49cb2a92a3ed94
Step6
Let’s make address field required. i.e we will not return profile without address.
We will use GraphQLNonNull type for this as explain here in detail https://medium.com/graphql-mastery/how-to-design-graphql-mutations-and-queries-part-2-enums-ebb01613832
Basically our code now looks like
var profileType = new graphql.GraphQLObjectType({
name: 'Profile',
fields: {
id: {
type: graphql.GraphQLID
},
name: {
type: graphql.GraphQLString
},
address: {
type: graphql.GraphQLNonNull(addressType),
resolve: () => {
return null
}
}
}
})
In the above code i am resolving “null” purposefully. This will return an error in current case, as we have defined addressType as nonnull.
Step7
Let’s suppose one user can have multiple address types so we need to return an array of address.
We need to use https://graphql.github.io/graphql-js/type/#graphqllist for this
Step 8
We can use enum types, to define our data better. e.g country we can use enum to restrict it to IN, US only
This how code looks for it
const countryEnumType = new graphql.GraphQLEnumType({
name: 'countryEnum',
values: {
IN: {
value: "IN",
},
US: {
value: "US",
}
},
});
var addressType = new graphql.GraphQLObjectType({
name: "address",
fields: {
id: { type: graphql.GraphQLID },
address: { type: graphql.GraphQLString },
country: { type: countryEnumType },
phone: { type: graphql.GraphQLString }
}
})
https://github.com/nodeexcel/gql_tutorial/commit/ad97bd73e9259868e4d42b65aed79120e4f26bce
Conclusion
In this blog post we have seen how to structure our GQL queries and also how build schema for our response.
At this stage, its important that you get a good hold on GQL queries. You can practice by making different json objects using GQL and trying it out.