An Overview of GraphQL with Apollo Server Compared with RESTful API

Hanwen Zhang
10 min readMar 28

--

A solution that uses one single endpoint fits all Query requests

Photo by Fahim Muntashir on Unsplash

Before we dive into GraphQL, it is important to understand what is API.

API

  • Application Programming Interface (API) — a connection that allows applications to talk to each other, enabling applications to exchange data and functionality easily and securely
  • Your Server -> Request through API -> Someone Else’s Server -> Response through API -> Your Server

REST API

  • In a type of web API service, the URL is indicative of the request itself.
  • Setup HTTP endpoint to allow user access and create/read/update/delete data in the database
  • Data entities live on a bunch of URLs on a server, when a request is received, the API responds with the full data payload of that entity

REST API Example

  • app.get('/tasks/:id', (req, res) => {})
  • Different URLs + HTTP verbs (= endpoints) for different actions

REST API Pros

  • Request path + HTTP method (= endpoints) identify a resource/action on the server
  • API is stateless and separate from any front-end (reusable)
  • The most common type of API because it is easy to use

REST API Cons

  1. We may need multiple entities at one time, each request does not have enough data for the request.
  2. Over fetching from API when we only want a small subset of data entity
  3. Endpoint — adding more endpoints, one endpoint per variation.

REST & HTTP methods

  • GET — get a source from the server
  • POST — post a resource to the server (create or append resource)
  • PUT — put a resource onto the server (create or overwrite a resource)
  • PATCH — update parts of an existing resource on the server
  • DELETE — delete a resource on the server

AJAX

  • Asynchronous JavaScript And XML, making requests behind the scene
  • Web applications can send and retrieve data from a server asynchronously without interfering with the display and behavior of the existing page
  1. Read data from a web server — after a web page has loaded
  2. Update a web page without reloading the page
  3. Send data to a web server — in the background

JSON

  • JavaScript Object Notation (JSON) — text-based format for representing structured data
  • Commonly used for transmitting data across a network, {} object, [] array, "key" and "value" pairs, separated by ,
  • Client Browser -> Request GET -> Your Server -> Request through API (Path, Parameter) -> Someone Else’s Server -> Response through API (DATA) -> Your Server -> Response POST -> Client Browser

JSON.parse() to convert the string into a JavaScript object

  • var obj = JSON.parse(jsonData);

JSON.stringify() to convert a JavaScript object into a JSON string

  • const jsonData = JSON.stringify(obj);

Limitation: we cannot copy the functions that are available in the target objects

Axios

  • Simple one-step process in response handling — only 1 promise
  • axios.get(url).then(response => console.log(response));

fetch()

  • Window Object (available from any scope), fetch used for data retrieval that uses the Promise API
  • fetching a resource from the network, returning a promise which is fulfilled once the response is available
  • 2-step process handing JSON data, return body with JSON content — need 2 promise

Fetch API

fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data));
.catch(error => console.error('There has been a problem with your fetch operation:', error));

fetch() Example

async function fetchMovies() {
const response = await fetch('/movies');
if (!response.ok) {
const message = `An error has occured: ${response.status}`; //404
throw new Error(message);
}
const movies = await response.json();
return movies;
}
fetchMovies().then(movies => {
movies; // fetched movies
}).catch(error => {
error.message; // 'An error has occurred: 404'
});

Send Data — fetch()

  • You can use fetch() to send POST requests
const addProductHandler = async (productName, productPrice) => {
try {
const newProduct = {
title: productName,
price: +productPrice //+ to convert string to number
}
let hasError = false;
const response = await fetch("localhost:5000/product", {
method: "POST",
body: JSON.stringify(newProduct),
headers: { //let server know that it is json data
"Content-Type": "application/json"
}
});
if(!response.ok){
hasError = true;
};
const responseData = await response.json();
if(hasError){
throw new Error(responseData.message);
};
setLoadedProducts(prevProducts => {
return prevProducts.concat({
...newProduct,
id: responseData.product.id
});
});
} catch (error) {
alert(error.message || "Something went wrong")
}
}

GraphQL

What is GraphQL

  • A query language allows the frontend communicates with the backend through API calls
  • Querying (reading/mutating data) exactly what you want from many resources in a single entry endpoint (request).
  • You can describe your data, describe a schema for your data, and request the exact data you need
  • The query has the same shape that you expect to return back from the API in JSON

How GraphQL works

The front end dictates what kind of data you want from the back end, what shape you want from it, and how you want it. It’s more powerful for the front end in this manner at the top level.

The format that we define in GraphQL always comes in the form of either asking for a mutation or a query.

Two things in GraphQL:

  • A query is a request for data.
  • A mutation is a request to modify or add data.

It’s all queries where you request data and it’s all mutations where you ask to update or create or delete data.

The Benefit of Using GraphQL over REST

  • Prevent over-fetching and under-fetching (more explanation below)
  • Self-documenting APIs (just by reading its schema), Fetching nested data in a single request
  • Schemas and Types: Describe our data and make it easy to discover, to understand what data is available in our API, and types ensure that the data that’s being passed back and forth in our queries is valid => Heavily typed (schema based)
  • Speeds up development for larger projects: As long as your data exists on the backend, you save yourself time waiting for new endpoints to be created and exposed on the backend

The Disadvantage of Using GraphQL over REST

  • Flexibility adds complexity: more time to do the initial setup and define all your schemas and types for small apps may not be worth the effort.
  • Difficult to cache: restful server can save a response with expired time and return that data without having to fetch it from our database, Instead, it is just sent from the cache, which is faster and which decreases the load on our server
  • Not RESTful: Not the vast majority of APIs that exist on the internet today, so many tools may not support GraphQL, it is a smaller and more niche player when it comes to APIs. However, you can have both in your application, use GraphQL as an endpoint to return all your data

Overfetching vs Underfetching

  • under fetching: only a single endpoint does not contain enough data to load the information we look for, then we need to make multiple round trips to the server and back before the page is fully loaded => we lose time making extra unnecessary round trips between the server and clients, we have to do more work querying multiple collections which slows down our page load time.
  • over fetching: we are sending unnecessary data over the network, from the server to the browser, and our front end then needs to filter the response from the server to get only the data that matters.
  • GraphQL solves over-fetching: We get exactly the data that we need and nothing more. This saves our bandwidth because we minimize the amount of data that we are returning from the server, and it saves us from doing extra processing work to filter this data
  • GraphQL solves under-fetching: We no longer have to make extra round trips to the server and back, so all that time waiting for our network connection is kept to a minimum.

GraphQL Architecture

Regardless of what page we are loading, in GraphQL, we ask for all the data that we need in a query, and we pass it off to the single GraphQL endpoint. our GraphQL code then goes ahead and wraps up all the data that we need in the back end using functions that GraphQL calls resolvers. When all our graphical resolvers have gathered all the data that we need to respond to our query, our server sends everything back to the front end.

GraphQL Server

  • Every GraphQL server has two main components — the schemas and the resolvers => what happens when we make a query
  • GraphQL always first determines if the query is valid by looking at our schemas and then it executes that query when executing a query => The value of each field is determined by calling a function called a resolver.
  • When a field is included in our query, the corresponding resolver is called to provide the value for that field.
  • When the server has called all the resolver functions found in the query, it then wraps all those values up and sends them back to the front end => back to the client that asked for the data in the first place.

Difference between REST API and GraphQL API

  • REST APIs offer multiple endpoints(URL + HTTP methods), you have one endpoint that does one task when the API call got hit, it is an architectural concept for network-based software
  • GraphQL provides the full capabilities of the exposed service, and offers a single endpoint but expects a query string in the request body

GraphQL Operation Type

  • query: entry point to read data, specify which endpoints we want to call, how we want the response to look
  • mutation: entry point to writing data, causes changes to the data available on the backend
  • resolvers: write code that resolves queries, fetches the data from the database, and can optionally accept four positional arguments: (parent, args, context, info)

GraphQL Concepts

  • Declaration — A GraphQL query begins with the Declaration (“query”)
  • Endpoint — A section of a GraphQL backend responsible for returning a specific piece of all the data available (“user”)
  • Fields — Properties that comprise the shape of a response (“name” and “age”)
  • Type — A collection of fields that make up a specific queryable object, the data that is available there
query {		//operation type
user { //operation endpoint
name //requested field
age! //an argument is required with exclamation point
}
}
query($year: Int = 2000) { //default value 2000
randomMovieByYear( year: $year ){
id
title
releaseYear
}
}
//JSON
{
"year": 2001 //use this one, overwrite the 2000 one
}
query {
movieOne: movieById( //Alias
movieId: "movie_0"
){
...movieDetails //fragment
}
movieTwo: movieById( //Alias
movieId: "movie_1"
){
...movieDetails //fragment
}
}
fragment movieDetails on Movie{
id
title
tagline
revenue
}

Mutation

type Mutation {
createMovie (
title: String!
tagline: String
revenue: Int
): Movie
addDirectorToMovie (
movieId: ID!
director: DirectorInput
): Movie
}

mutation($directorToAdd: DirectorInput!){
addDirectorToMovie(
movieId: "movie_0"
director: $directorToAdd //using the query variable with $, identify variables inside of a query
){ //the field we want to return followed by the endpoint
title
tagline
directors{
id
name
}
}
}

exports.typeDefs = gql`
type Mutation {
addCategory(input: AddCategoryInput!): Category! //Category! is the type of what we return
}
input AddCategoryInput { //type for the input
name: String!
}
`
exports.Mutation = {
addCategory: (parent, { input }, { db }) => {
const { name } = input;
const newCategory = {
id: uuid(), //const { v4: uuid } = require("uuid");
name,
};
db.categories.push(newCategory);
return newCategory;
}
}

Apollo Server

  • A state management library that allows you to write GraphQL queries and then see the results automatically updated in your UI
  • npm install apollo-server graphql
  • String, Int, Float, Boolean, ID (input something inside property) //scalar type, null included
const { ApolloServer, gpl } = require("apollo-server");
const typeDefinitions = gql` //what in our data we are going to defined, how our data is going to look
type Query {
hello: String //can include null without !, it can be string or null without !
products(filter: ProductsFilterInput): [Product!]! //array of object in Product type
product(id: ID!): Product //query with variable, with !, you have to include something
}

type Product { //object type
id: ID!,
price: Float!
}

input ProductsFilterInput {
onSale: Boolean
}
`
const resolvers = { //functioning return the data defined in our typeDefinitions
Query: {
hello: () => "World!",
products: (parent, {filter}, {products}) => { //when we do filter
let filteredProducts = products;
if(filter){
if(filter.onSale === true){
filteredProducts = filteredProducts.filter(prod => prod.onSale;)
}
}
return filteredProducts;
},
product: (parent, args, context) => return products.find(prod => prod.id === args.id);
},
Category: { //relating data - pseducode
products: (parent, args, context) => { //using parent for the category that we call with
const categoryId = parent.id;
return products.find(prod => prod.categoryId === categoryId);
}
}
}
const server = new ApolloServer({
typeDefinitions,
resolvers: { //no needs for the object if in the same file
Query,
Category
},
context: { //globally available
categories,
products
}
});
server.listen().then(({ url }) => {
console.log("Server is ready at" + url)
})

GraphiQL

  • An interface for editing and testing GraphQL queries/mutations

Add the GraphiQL option as true, then npm start the server, just go to the /graphql endpoint, which the tool interface will show up.

Summary

We can get all data using a single query to our back-end server. We didn’t have to make multiple requests to endpoints which would be needed in RestAPI. We would have to put all of this information together on the front end after making all requests against the backend for each collection of data. Whereas with GraphQL, we only needed to know the shape of the data that are available in the back end.

--

--

Hanwen Zhang

Full-Stack Software Engineer at a Healthcare Tech Company | Document My Coding Journey | Improve My Knowledge | Share Coding Concepts in a Simple Way