In a RESTful world, we should be aware of API versioning, over-fetching, waterfall requests, and so on. GraphQL is an open-sourced query language and a server-side runtime. It is designed to create fast, flexible, and developer-friendly APIs. It uses queries to read data and mutations to develop, update, and delete data.
Getting Started
We have been using REST to build APIs for a long time. In a RESTful world, you might face some challenges like:
- API versioning
- A lot of endpoints
- Over and under fetching of information
- Waterfall requests etc. GraphQL is the simplest solution for the above challenges. GraphQL has become a new standard for API development. It is a query language for APIs which enables users to specify their requirements and fetches nothing more, nothing less.
GraphQL
GraphQL is a query language and server-side runtime for APIs. As an alternative to REST, it lets developers to create requests that pull data from multiple data sources in a single API call. It is incredibly effective for building modern mobile and web applications. It requires less branching than REST. It ensures that the application loads only the relevant data, even if it’s from multiple sources.
Features
Strongly typed schema
We can specify all data types (such as Boolean, String, Int, Float, ID, Scalar) supported by the API in a schema in GraphQL Schema Definition Language (SDL). It makes GraphQL less error-prone, more validated, and provides auto-completion for supported IDEs.No over fetching or under fetching
Over-fetching occurs when the response fetches more than what is required. Under-fetching occurs when the response doesn’t fetch adequate data in a single API request and needs additional API requests to get the related or referenced data.
In GraphQL, you can specify the exact fields that you want to fetch. It fetches the required fields in a single request and solves the issues of over-fetching and under-fetching.
Saves time and bandwidth
GraphQL allows us to make multiple resource request in a single query call. It saves a lot of time and bandwidth by reducing the number of requests to the server. It also helps to reduce waterfall network requests.Schema stitching for combining schemas
Schema stitching is used to combine multiple schemas into a single schema. In a microservices architecture, each microservice can define its own GraphQL schema, and combine into a single unit that is accessed by the client.No versioning
In REST architecture, maintaining version is a common practice as developers create new versions due to resource change or structural changes in request/response. Whereas in GraphQL, there is no such requirements where you can add or remove fields, but the resource URL remains the same.GraphQL vs REST
In REST APIs, you typically have a specific endpoint that returns an entire block of data as a JSON response, which needs to be parsed. Whereas in GraphQL, you use schema, queries, and resolvers for requesting a specific data and not just an entire block. It does not require the data to be parsed or send multiple requests to multiple resources.
Fundamentals of GraphQL
Let’s discuss some of the fundamentals of GraphQL such as schema, types, queries, mutations and subscriptions.
Schema
A schema defines the type of the GraphQL API such as objects, fields, relationships that a client can access. Each request from the client is validated and executed against the schema. You can use the buildSchema function to build a Schema object in GraphQL.
In the GraphQL, a user with an id, name, and age can represents as below,
type User {
id: ID!
name: String!
age: Int
}
Types
You can define different types inside buildSchema like type Query {...}
and type Mutation {...}
. type Query {...}
holds functions mapped to GraphQL queries which is used to fetch data (equivalent to GET in REST). type Mutation {...}
holds functions mapped to mutations which is used to create, update, or delete data (equivalent to POST, UPDATE, and DELETE in REST).
You can return a user and an array of users of type Person, who has an id, name, age as below:
// Initialize a GraphQL schema
var schema = buildSchema(`
type Query {
user(id: Int!): Person
users(age: Int): Person
},
type Person {
id: Int
name: String
age: Int
}
`);
Queries
Queries describe how you’re going to get data. It returns only the data you have specified.
You can get a user of type Person with id and age as below:
type Query {
user(id: Int!): Person
users(age: Int): Person
}
Resolvers
Resolver is designed to map the operation to an actual function.
For example, you have a user object inside type Query and map it to a function with the same name inside root as follows,
// Sample users
var users = [
{
id: 1,
name: 'Vishnu',
age: '27'
},
{
id: 2,
name: 'Amal',
age: '26'
},
];
// Return a single user
var getUser = function(args) {
// ...
}
// Return a list of users
var retrieveUsers = function(args) {
// ...
}
// Root resolver
var root = {
user: getUser, // Resolver function to return user with specific id
users: retrieveUsers
};
Aliases
Aliases enables us to rename the data that is returned in a query’s results. It does not change the original schema but display it in accordance with the specifications. It is useful when you have more than one top level field in the same query with different arguments.
query getUsers {
male: users(gender: "MALE") {
id
name
age
}
female: users(gender: "FEMALE") {
id
name
age
}
}
Fragments
Fragments are reusable units which enables to construct sets of fields and include them in queries wherever required.
To create a user fragment, use the following code snippet,
query getUsersWithFragments($userAID: Int!, $userBID: Int!) {
userA: user(id: $userAID) {
...userFields
},
userB: user(id: $userBID) {
...userFields
}
}
fragment userFields on Person {
name
age
}
In the above code, a fragment called userFields
is created to retrieve user information. You will receive the following output while running the above code:
{
"data": {
"userA": {
"name": "Vishnu",
"age": 27
},
"userB": {
"name": "Amal",
"age": 26
}
}
}
Directives
Directives enable you to dynamically change the structure of your queries using variables. Sometimes, you might skip or include some fields without altering the schema in your query. For which, you can use the following directives.
-
@include(if: Boolean)
- Includes the field if true. -
@skip(if: Boolean)
- Skips the field if true. For instance, if you want to retrieve users who are fans of Vishnu with their ids but you don’t require their age field, follow the below code snippet.
query getUsers($age: Boolean!, $id: Boolean!) {
users(age: $age){
...userFields
}
}
fragment userFields on Person {
id @include(if: $id)
name
age @skip(if: $age)
}
Provide the following content in the variables panel on the web interface:
{
"shark": "Vishnu",
"age": true,
"id": true
}
You will obtain the following output:
{
"data": {
"users": [
{
"name": "Vishnu",
"id": 1
},
{
"name": "Amal",
"id": 2
}
]
}
}
Mutations
Mutations is a GraphQL operation which deals with creation, deletion, and updation of data.
For instance, you require to update a user with id == 1 and change their age and name. Update the user schema with a mutation type as follows,
// Initialize a GraphQL schema
var schema = buildSchema(`
type Query {
user(id: Int!): Person
},
type Person {
id: Int
name: String
age: Int
}
# newly added code
type Mutation {
updateUser(id: Int!, name: String!, age: String): Person
}
`);
Add a updateUser function to update the user details:
// Update the user details
var updateUser = function({id, name, age}) {
users.map(user => {
if (user.id === id) {
user.name = name;
user.age = age;
return user;
}
});
return users.filter(user => user.id === id)[0];
}
Update the root resolver with relevant resolver functions as follows:
// Root resolver
var root = {
user: getUser,
users: retrieveUsers,
updateUser: updateUser
};
Assuming these are the initial user details:
{
"data": {
"user": {
"name": "Vishnu",
"age": 27,
}
}
}
Add the following query to the input panel on the web interface:
mutation updateUser($id: Int!, $name: String!, $age: String) {
updateUser(id: $id, name:$name, age: $age){
...userFields
}
}
fragment userFields on Person {
name
age
}
Provide the following content in the variables panel on the web interface:
{
"id": 1,
"name": "Codemaker",
"age": "27"
}
You will obtain the following output:
{
"data": {
"updateUser": {
"name": "Codemaker",
"age": 27
}
}
}
The user with the id == 1
has been updated from Vishnu (age 27) to Codemaker(age 27).
Develop your first GraphQL API
We have discussed the fundamentals of GraphQL so far. It’s time to create our first GraphQL api with node.js and express.
Install dependencies
We require node.js to be installed in the machine to start with GraphQL.
- Download Node.js from the official site.
- Follow the Installation steps to install it in your machine.
Setting up the environment
- Create a
first-graphql-api
directory where we can hold our apis.
mkdir first-graphql-api
cd first-graphql-api
- Initialize new npm project using the following command,
npm init -y
- Create a
index.js
file which will contain the server code. - Install the required packages
graphql
,express
andexpress-graphql
to the project using the following command,
npm install graphql express express-graphql
- Open index.js file and add the following code to it,
var express = require('express');
var { graphqlHTTP } = require('express-graphql');
var { buildSchema } = require('graphql');
const port = process.env.PORT || 4000
// Initialize a GraphQL schema
var schema = buildSchema(`
type Query {
hello: String
}
`);
// Root resolver
var root = {
hello: () => 'Hello world!'
};
// Create an express server and a GraphQL endpoint
var app = express();
app.use('/graphql', graphqlHTTP({
schema: schema, // Must be provided
rootValue: root,
graphiql: true, // Enable GraphiQL when server endpoint is accessed in browser
}));
app.listen(port, () => {
console.log(`App running on port ${port}.`)
})
- Add a start command on scripts in the package.json as follows,
"start": "node index.js"
The final project structure should look like the following,
- Start the node server using the following command,
npm start
- Open the link
localhost:4000/graphql
on a web browser. You will see a Welcome to GraphiQL web interface.
The GraphiQL interface is mainly divided into two. The left panel is used to enter the queries and query variables. The right panel is used to display the executed query results. The play button located on the top left corner is used to execute queries.
Execute your GraphQL Apis
1. GraphiQL web interface
Let’s add a query { hello }
in the left panel and click on play button. You will get the result hello world!
in the right panel (shown in the above figure).
We have tried the GraphiQL web interface so far. Now, we can get the results via an api call without GraphiQL interface. For which, set graphiql
value to false
in the index.js
file.
If you run the server again and hit the graphql api endpoint, you will get the following error,
This is because we haven’t yet provided the query string in the api.
2. Using CURL
To send a HTTP POST request to the endpoint with express-graphql
, pass the GraphQL query as the query field in the JSON payload.
For example, if the code is running on an Express GraphQL server and the endpoint is http://localhost:4000/graphql
, we can add query { hello }
query as the payload in the curl command as follows:
curl -X POST -H "Content-Type: application/json" -d "{\"query\": \"{ hello }\"}" http://localhost:3001/graphql
If you execute the above command on a terminal, the output is returned as JSON and is as follows:
{"data":{"hello":"Hello world!"}}
If you prefer to use a GUI to test your queries, use clients such as GraphiQL and Insomnia.
3. From web browser console
To send GraphQL queries from the browser.
- Open the URL http://localhost:4000/graphql on a browser.
- Open a developer console (Press F12 in Google Chrome).
- Paste the following code in the console window,
fetch('/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({query: "{ hello }"})
})
.then(r => r.json())
.then(data => console.log('data returned:', data));
4. Postman
To execute GraphQL queries using Postman, type the URL in the input field and provide the query in the body.
5. From an HTML file
To get response in the frontend using fetch api, create a file named fetch_demo.html
and add the following code to it.
<!DOCTYPE html>
<html>
<body>
<h2>GraphQL fetch demo</h2>
<p>This example returns the response from the server using GraphQL API</p>
<p id="demo"></p>
<script>
function fetchData() {
fetch('http://localhost:4000/graphql', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({query: "{ hello }"})
})
.then(r => r.json())
.then(data => { document.getElementById("demo").innerHTML = "Response: " + JSON.stringify(data); console.log('data returned:', data) });
return res
}
fetchData()
</script>
</body>
</html>
- You can either put the file in a server or bind the file in the same express server that we are using for creating the GraphQL apis.
- Add an Api in the index.js file to render the HTML file as follows,
app.get('/graphql-fetch', function(request, response){
response.sendFile("fetch_demo.html", { root: '.' });
});
- Hit the
graphql-fetch
api on your browser to see the following response,
Thanks for reading this article.
Thanks Gowri M Bhatt for reviewing the content.
If you enjoyed this article, please click on the heart button ♥ and share to help others find it!
The full source of the demo and this tutorial is available on
Here are some useful links,
Top comments (0)