The main purpose of this server-client Node.js project is to help other people understand how GraphQL exposes the data from the Server and the Client fetches it. I have tried to make it as simple as possible- if you want to dive into the code of the project you can find it here.
Now, straight to the point: GraphQL is a query language for APIs developed and open-sourced by Facebook to speed up the request process.
While REST has been a popular way to expose data from a server, instead of having multiple endpoints that return fixed data structures, GraphQL just has a single endpoint, and it is the clientβs job to specify what data it needs from it.
Table of Contents
- Getting started
- Define the Schema
- Add Resolver function
- Set up the Server
- Set up the Client
- Fetching data from the server
- Displaying the data
- Conclusion
- Useful resources
Getting started
The first step is to download and install Node.js in case you havenβt already. Once you have it installed letβs begin with the directory structure. The project will be composed of two directories, one for the Client and another for the Server. I have chosen to keep both inside the project root directory but then you can split it into two separate projects or any way you want.
π project
βββ π client
βββ π server
Now we will initialize the project in the server directory. Change the location to the server folder in your terminal and run npm init
to fill in the project info and generate the package.json file. Or npm init -y
which tells the generator to use the defaults (instead of asking questions and simply generate an empty npm project without going through an interactive process).
The next step will be to install GraphQL.js and Apollo Server to our server. GraphQL.js will provide two important capabilities:
- Building a type schema, which we will do in the next step.
- Serving queries against that type schema.
To install it just run npm install graphql
. I am assuming you are using a version of NPM equal or higher than 5.0.0 so you do not need to add --save
when installing a dependency to be saved in the package.json
.
Apollo Server, on the other hand, will help us to implement the GraphQL functionalities. It is part of the Apollo Data Graph Platform.
Apollo is a platform for building a data graph, a communication layer that seamlessly connects your application clients (such as React and iOS apps) to your back-end services. Is an implementation of GraphQL designed for the needs of product engineering teams building modern, data-driven applications. - Apollo Documentation
What you need to know about Apollo, at least for now, is itβs a community that builds on top of GraphQL and provides different tools to help you build your projects. The tools provided by Apollo are mainly 2: Client and Server.
-
Apollo Client helps your Frontend communicate with a GraphQL API. It has support for the most popular frameworks such as React, Vue, or Angular and native development on iOS and Android.
-
Apollo Server is the GraphQL server layer in your backend that delivers the responses back to the client requests.
Now that you understand Apollo better and why we will use it, letβs continue setting up GraphQL.
Define the Schema
A GraphQL Schema is at the core of any GraphQL server implementation. It describes the shape of your data, defining it with a hierarchy of types with fields that are populated from your data source and specifies which queries and mutations are available, so the client knows about the information that can be requested or sent.
For example, if we wanted to build a music application, our simplest schema, usually defined in a schema.graphql
file, would contain two Object types: Song
and Author
like this
type Song {
title: String
author: Author
}
type Author {
name: String
songs: [Song]
}
Then we would have a Query type to define the available queries: getSongs
and getAuthors
, each returning a list of the corresponding type.
type Query {
getSongs: [Song]
getAuthors: [Author]
}
To keep it as simple as possible our schema will have just a single Query type which will return a String
.
type Query {
greeting: String
}
We can use any programming language to create a GraphQL schema and build an interface around it but as I explained before we will use Apollo server to execute GraphQL queries. So we create a new server.js
file in the server directory to define the Schema on it.
π project
βββ π client
βββ π server
βββ π server.js
Now we install apollo-server running npm install apollo-server
We have to import the tag function gql
from apollo-server to parse the schema this way: const {gql} = require('apollo-server');
and then declare a typeDefs
constant which is an abstract syntax tree of the Graphql code.
When a GraphQL server receives a query to process it generally comes in as a String. This string must be tokenized and parsed into a representation that the machine understands. This representation is called an abstract syntax tree.
If you want to learn more about abstract syntax trees AST Explorer is an online tool that lets you explore the syntax tree created by a chosen language as a parser.
The server.js
file would look like this.
const { gql } = require('apollo-server');
const typeDefs = gql`
type Query {
greeting: String
}
`;
Add Resolver function
Now we have defined our Schema we need a way to answer the client requests for that data; the resolvers. A resolver is a function that handles the data for each one of the fields of your schema. You can send that data to the client by fetching a back-end database or a third-party API among others.
They have to match the type definitions of the Schema, in our case, we just have one type definition Query which returns a greeting of type String
, so we will define a resolver for the greeting
field, like so:
const resolvers = {
Query: {
greeting: () => 'Hello GraphQL world!π',
},
};
As I explained at the beginning we will keep this example as simple as possible but keep in mind that in a real case here is where you have to make the queries to the database, external API, or from wherever which you intend to extract the query data.
Set up the Server
In the same server.js
, we define and create a new ApolloServer
object passing the Schema
(typeDefs) and resolvers
as parameters.
const { ApolloServer, gql } = require('apollo-server');
const server = new ApolloServer({ typeDefs, resolvers });
Then calling the listen
method we start the server on the port
that we specify in the params.
server
.listen({ port: 9000 })
.then(serverInfo => console.log(`Server running at ${serverInfo.url}`));
We can also destructure the ServerInfo url
when logging it.
server
.listen({ port: 9000 })
.then(({ url }) => console.log(`Server running at ${url}`));
The server.js
file should look like this right now.
const { ApolloServer, gql } = require('apollo-server');
const typeDefs = gql`
type Query {
greeting: String
}
`;
const resolvers = {
Query: {
greeting: () => 'Hello GraphQL world!π',
},
};
const server = new ApolloServer({ typeDefs, resolvers });
server
.listen({ port: 9000 })
.then(({ url }) => console.log(`Server running at ${url}`));
Now if we run node server/server.js
we will finally have our GraphQL server up and runningπ!
You can go and check it on http://localhost:9000/
~/graphql-hello-world-server
> node server/server.js
Server running at http://localhost:9000/
For the first time using GraphQL, you may be thinking what is this application I am seeing in front of me if we have not written a single line of client code?.
The answer to that question is the GraphQL Playground.
GraphQL Playground is a graphical, interactive, in-browser GraphQL IDE, created by Prisma and based on GraphiQL. - Apollo docs
But what does that mean? It means that this is an environment where we can perform Queries, Mutations, or Subscriptions to our schema and interact with its data.
If you have worked with RESTful requests before this would be some kind of equivalent to Postman, just that here you do not have to download and configure anything, it just comes by default with Apollo!
So letβs try it!
- On the left panel write the
greeting
query we defined in our schema. - Then press the βΆ button that is in the middle.
- And Voila! On the right panel appears the data we defined in our resolver to return.
Set up the Client
Now we have our server up and running letβs focus on the client part. We will start creating a client.html
file inside our client folder.
π project
βββ π client
| βββ π client.html
βββ π server
βββ π server.js
The index.html
file will have the basics of any HTML
file and a loading header <h1>Loading...</h1>
to show the user something while we request the data from the server.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Hello World GraphQL Client</title>
</head>
<body>
<h1>Loading...</h1>
<script src="app.js"></script>
</body>
</html>
Fetching data from the server
First off, in the same client folder, we create an app.js
file where we will write the client logic to fetch the data from the server.
π project
βββ π client
| βββ π client.html
| βββ π app.js
βββ π server
βββ π server.js
Inside it, we set the server URL to which we will make the request.
const GRAPHQL_URL = 'http://localhost:9000/';
Next, we define our async function fetchGreeting()
to, fetch the greeting from the server. We will use the fetch API to perform the HTTP request which by default returns a promise to which we can subscribe and get the answer asynchronously.
async function fetchGreeting() {
const response = await fetch(GRAPHQL_URL, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
query: `
query {
greeting
}
`,
}),
});
const responseBody = await response.json();
console.log(responseBody);
}
A detail to take into account is that the method of the request is POST
, which can confuse us if we are used to working with RESTful
because this same request made in RESTful
, where we just want to read information from the server, would be usually done with the method GET
.
The thing is that with GraphQL we always make POST
requests where we pass the query in the payload (body).
Finally, we just call our method fetchGreeting();
const GRAPHQL_URL = 'http://localhost:9000/';
async function fetchGreeting() {
const response = await fetch(GRAPHQL_URL, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
query: `
query {
greeting
}
`,
}),
});
const responseBody = await response.json();
console.log(responseBody);
}
fetchGreeting();
If you open the file in your browser and see the console on the developer tools you can see that we actually got the greeting data from the query π!
Displaying the data
Now we have successfully got the data from the server lets update the loading title. The first thing we will do is destructure the response and return just the data
from it.
Just replace this part of the code
const responseBody = await response.json();
console.log(responseBody);
With this one
const { data } = await response.json();
return data;
Then we will update the title with the greeting
returned inside the data from the response
fetchGreeting().then(({ greeting }) => {
const title = document.querySelector('h1');
title.textContent = greeting;
});
So our app.js
file will end up having this look
const GRAPHQL_URL = 'http://localhost:9000/';
async function fetchGreeting() {
const response = await fetch(GRAPHQL_URL, {
method: 'POST',
headers: {
'content-type': 'application/json',
},
body: JSON.stringify({
query: `
query {
greeting
}
`,
}),
});
const { data } = await response.json();
return data;
}
fetchGreeting().then(({ greeting }) => {
const title = document.querySelector('h1');
title.textContent = greeting;
});
Our index.html
will have the loading title updated with the data fetched from our server!π
Conclusion
I hope you enjoyed the post and that this project has fulfilled the objective of showing how graphql works under the hood, at least in a very simple way. I know there are a lot of things that I have not explained or that I could have gone deeper but like any hello world
project, it is intended for people who are beginners with it, so I wanted to keep it as simple as possible.
Iβm looking forward to learning more about GraphQL and using it in future projects. If youβve got any questions, suggestions, or feedback in general, donβt hesitate to reach out on any of the social networks from my site or by mail.
Useful resources
Here is a collection of links and resources which have been useful for me to improve and learn more about GraphQL
Docs π
- Project source code - The Github repository with all the code from the project.
- GraphQL main site - GraphQL main site.
- Apollo documentation - The Apollo platform docs.
Learn π
- How to GraphQL - Free and open-source tutorials to learn all around GraphQL to go from zero to production.
- GraphQL by Example - Great course where you learn GraphQL by writing full-stack JavaScript applications with Node.js, Express, Apollo Server, React, Apollo Client.
- Introduction to GraphQL - A series of articles to learn about GraphQL, how it works, and how to use it.
Tools π§
- Apollo GraphQL - Main site of the Apollo GraphQL implementation.
- GraphQL Playground - Repository of the GraphQL Playground IDE we used on the project.
IDEs π»
- JS GraphQL - WebStorm and other IntelliJ-based IDEs plugin to support GraphQL language including tagged template literals in JavaScript and TypeScript.
- GraphQL - GraphQL extension for VSCode adds syntax highlighting, validation, and language features like go to definition, hover information, and autocompletion for GraphQL projects. This extension also works with queries annotated with gql tag.
- GraphQL for VSCode - VSCode GraphQL syntax highlighting, linting, auto-complete, and more!
Extras π
- GraphQL APIs - A list of public GraphQL APIs to test your skills or to build something with them.
- GraphQL: The Documentary - A 30 min video that explores the story of why and how GraphQL appeared, and the impact itβs having on big tech companies worldwide, including Facebook, Twitter, Airbnb, and Github.
Comments
Thanks for reading β€οΈ
You can comment by replying to the issue for this post.
Thereβs no comments yet, be the first to leave one!