Introducing the Apollo GraphQL Platform for implementing the GraphQL Specification
This article is part 5 of the series on Exploring GraphQL. Check out the other articles:
- Part 1: What is GraphQL and why Facebook felt the need to build it?
- Part 2: Fundamentals of GraphQL
- Part 3: Building a GraphQL Server using NodeJS and Express
- Part 4: How to implement Pagination and Mutation in GraphQL
- Part 6: How to Connect MongoDB to a GraphQL Server?
- Part 7: GraphQL Subscriptions - Core Concepts
- Part 8: Implementing GraphQL Subscriptions using PubSub
GraphQL is a query language and an execution run-time for APIs. It has a definite Type System, which makes it convenient to build schemas for various resources. GraphQL is declarative in nature and gives the client the power to ask exactly for what they need! It solves some of the problems of the traditional REST architecture like multiple end-points, over-fetching and under-fetching of resources, versioning, etc. Facebook built GraphQL in 2011 to solve the problems faced by its internal applications, and then open-sourced it in 2015. GraphQL has now gained the trust of startups to big companies like Intuit, Pinterest, Shopify, etc. You can read more on What is GraphQL and Why Facebook felt the need to build GraphQL?
GraphQL as a Specification
In the last few articles, we used graphql
NPM module to define and execute GraphQL operations (queries and mutation). We also learned that GraphQL uses the concept of resolvers to fetch data for a respective field. One important thing to understand here is -- GraphQL is a Query language and Specification, and not an implementation of any technology per se, which means it is language-agnostic and can be implemented in any language of your choice. It is a Query language and defines the grammar for understanding its various constructs. For example, the specification clearly states the definition of Selection Sets as:
An operation selects the set of information it needs, and will receive exactly that information and nothing more, avoiding over‐fetching and under‐fetching data.
The Specification defines the selection set and its role in executing the queries. The implementation details may vary for different GraphQL clients.
But What is the GraphQL NPM module that we've been using?
The GraphQL NPM module is built by Facebook as a reference implementation in JavaScript. It is great to get up and running with the basic features in GraphQL. The graphql
GitHub repository contains various sub-modules for implementing the core functionalities like:
Module | Use |
---|---|
graphql/language | Parse and operate on the GraphQL language |
graphql/type | Define GraphQL types and schema |
graphql/validation | The Validation phase of fulfilling a GraphQL result |
graphql/execution | The Execution phase of fulfilling a GraphQL request |
graphql/error | Creating and formatting GraphQL errors |
graphql/utilities | Common useful computations upon the GraphQL language and type objects |
graphql/subscription | Subscribe to data updates |
The graphql
module does the complex work of defining the types, implementing schema, parsing, validating and executing the queries. Some of the projects like Relay, Apollo GraphQL have used this core module and built a concrete GraphQL implementation in many different languages like JavaScript, Python, Java, etc.
What is Apollo GraphQL?
The Apollo GraphQL platform is an implementation of the GraphQL specification and uses graphql
as one of the important dependencies. It comes bundled with Apollo Server, Apollo Client, and some excellent tooling modules to help write efficient and better code. The platform is convenient for developers to implement the complex GraphQL functionalities like Pagination, subscriptions, Caching, Authorization, and many more. We're going to look at each of these functionalities in detail soon! Let's first understand what Apollo Server is.
Apollo Server
Apollo Server can easily be integrated with most of the NodeJs HTTP server frameworks like Express, Koa, Hapi, Amazon Lambda, etc. The Apollo Server Express is the most popular implementation of the GraphQL server and we'll learn more about it in detail in this article.
In the last article, while building a Food Ordering System, we used expressGraphQL
as middleware on the express server. The expressGraphQL
middleware takes in the schema
object.
To understand how Apollo Server fits in, we'll have to understand the working of middleware in detail. Let's do that right away:
A Middleware is a function that has access to the request and response objects and the next middleware function. It can intercept the incoming request and perform some computations on it to send the appropriate response. So, a middleware can:
- Execute any code.
- Make changes to the request and the response objects.
- End the request-response cycle.
- Call the next middleware function in the stack.
You can read more on using middleware in express here.
The expressGraphQL
middleware should now make sense! It processes the incoming request on the graphql endpoint and accordingly executes the respective resolvers for the required fields.
Apollo Server works as middleware and has the power to perform computations on the request object and send back the appropriate response. Please note: The Apollo server can also be implemented as a stand-alone integration. The Apollo server uses the same concept of Schema and Resolvers that we've seen in the previous articles. Let's recap that quickly and write our minimal GraphQL server implementation using apollo-server
.
Schemas & Resolvers in GraphQL
Schema is used for defining the structure of an entity. The GraphQL uses Schema Definition Language for implementing the schemas.
Resolvers are functions that are used for fetching the data from the data source for the various fields of the query.
Setting-up the Apollo Server
Let's install the dependencies first:
shellnpm install apollo-server graphql
We'll have to install graphql
along with apollo-server
because graphql
is its peer dependency.
Let's write our simple Apollo server:
javascriptconst { ApolloServer, gql } = require('apollo-server') const typeDefs = gql` type Query { userName: String } ` const resolvers = { Query: { userName: () => 'John' } } const server = new ApolloServer({ typeDefs, resolvers }) server.listen(3000, () => { console.log('Your Apollo Server is running on port 3000') })
Here we're using the stand-alone method to implement the Apollo Server.
The ApolloServer
method and the gql
tag are imported from the apollo-server
module. The ApolloServer
method takes in an object that contains the type definitions and resolvers. The gql
tag is used for defining the schema into a multi-line template and it parses the template to generate an Abstract Syntax Tree.
The Query
is a type and has one field userName
of type String
. The resolvers are defined in an object and each of the fields is resolved by using its respective function. Notice how the userName
field is resolved to get a string John
as an output. And the last statement should look familiar; we're running the server on port 3000.
Apollo Client
The apollo-client
module is used on the client-side to build UI components that fetch data using GraphQL. It is one of the most popular modules to integrate GraphQL on the client-side and has around a million downloads on NPM. It can be integrated with most of the popular JavaScript libraries/frameworks like React, Angular, Vue, etc.
The companies like Airbnb, Netflix, The New York Times are using Apollo Client in production. The New York Times switched from Relay to Apollo Client because of its vibrant ecosystem, better server-side rendering, preloading/prefetching of certain queries, code-splitting at the component-level, persisted queries, etc. You can read more about their experience of switching to Apollo here.
The front-end applications are now getting build in modern libraries/frameworks like React, Angular, etc. These frameworks reduce a lot of boilerplate code of setting up the initial state and updating the DOM nodes, which was very difficult to achieve with the traditional libraries like JQuery.
The modern frameworks help in implementing complex functionality on the front-end. But to implement them, we're shipping a lot of JavaScript code over the wires, which in turn can worsen the performance of the application. The developers have to come up with clever ways to handle Caching, Data Normalization, etc.
State Management in React is hard and developers spend a lot of time in integrating the Redux boilerplates and many other features. Here's the slide from Peggy Rayzis presentation while she was explaining the cool new features in Apollo.
Image loading...
Apollo Client does the heavy-lifting and makes Data Fetching, Caching, Authorization and other complicated features a breeze! Now you can easily integrate these features in your applications using Apollo Client.
We'll be using the React integration with Apollo Client for building the front-end of the Food Ordering System. If you've some experience in using React, you'd know that Data management in React is a big deal! You'll have to integrate Redux and dispatch appropriate actions to fetch data and show the loading or error states accordingly.
The Apollo Client is easy to set-up and all your state management logic is handled by the underlying mechanism.
Let's write a simple component that uses React Apollo Client to manage its data:
javascriptconst getUser = () => { <Query query={user}> { ({ loading, error, data }) => { if (error) return <Error /> if (loading || !data) return <Loader /> return <User data={data.userDetails} /> }} </Query> }
The getUser
is a stateless component and the Query
component is imported from the Apollo client. It takes in one prop query
. The next line is a function that accepts an object and we're using the ES6 Object destructing technique to get loading
, error
and data
. The components Error
, Loader
, User
are rendered based on these three values. You don't have to write reducers, action dispatchers to fetch data and store the state of the application. The Query
component will do that out of the box! We'll get into the details of the client-side application in the next set of articles. Stay tuned!
Integrating the Apollo Platform
I hope, you're now convinced on using the Apollo Platform to implement the GraphQL specification! Let's start building our Food Ordering System in Apollo. If you're stuck anywhere in the code below, you can refer my GitHub respository to get to speed.
Installing the Dependencies
shellnpm install express apollo-server-express graphql --save
express
: a Node.js web application framework that is used for building web and mobile applications.
apollo-server-express
: a module for integrating the express server with Apollo.
graphql
: an important peer dependency of apollo-server-express
and is used for executing queries on the schema.
Let's also install nodemon
as a dev-dependency. It automatically restarts the server when it detects a change in the application.
shellnpm install nodemon --save-dev
Let's add a script in package.json
to run server.js
on npm start
command. Put this in your package.json
file in the scripts
object:
javascriptstart: "nodemon server.js"
Setting-up the Server
Create a new file server.js
in the root directory and add the below code:
javascriptconst express = require('express') const { ApolloServer, gql } = require('apollo-server-express') const { typeDefs } = './schema' const { resolvers } = './resolvers' const app = express() const server = new ApolloServer({ typeDefs, resolvers }) server.applyMiddleware({ app }) app.listen({ port: 3000 }, () => { console.log('Your Apollo Server is running on port 3000') })
We're using the Middleware approach to integrate the ApolloServer
with the express application. As we've seen earlier, ApolloServer
accepts an object that contains the schema and resolvers. server.applyMiddleware({ app })
does the magic of connecting our express app with the apollo server!
The Apollo Server can intercept the incoming request object, perform computations on the query against its schema and send the appropriate response.
You'll get an error when you try to start the application because we've not added the schema
and the resolvers
file yet. Let's do that right away!
Defining Schemas
GraphQL uses Schema Definition Language to define types. It supports Scalar types like Int
, String
, Float
, Boolean
, ID
and object types like query
, mutation
and subscription
. If you are not familiar with the Type System in GraphQL, please refer this link.
We're building a Food Ordering System and its use-case is:
A user should be able to browse a list of restaurants. He can see the name, location, contact, email address and the menu for these restaurants. He can place an order for any item from the menu of his favorite restaurant. The customers’ order should be mapped accordingly with the restaurants.
You can read this article to get an overview of the project.
Create a schema.js
file in the root directory. Let's start by defining the Restaurant
type:
javascriptconst { gql } = require('apollo-server-express') const typeDefs = gql` type Menu { id: ID, name: String, price: Float } type Restaurant { id: ID, name: String, email: String, location: String, menu: [Menu] } type Query { restaurants: Restaurant } ` module.exports = typeDefs
The type Query
is the root type and it contains one field restaurants
of type Restaurant
. The structure of types is same as the previous article. There is one difference in the above schema and that of the previous implementation--we've not defined resolvers for any of the fields. We'll define resolvers for each field in a separate file resolvers.js
.
Let's add two more types Customer
and Order
to complete our schema:
javascripttype Customer { id: Int, name: String, email: String, location: String } type Order { id: Int customerId: Int restaurantId: Int order: [String] }
Here's the entire code in schema.js
file.
Defining Resolvers
The Query
operation in GraphQL fetches data from one or more data sources while the mutation
makes side-effects on the server.
Resolvers are used for instructing these operations to fetch/mutate data on the server. The resolvers
object is a map of type fields and a resolve
function is implemented for each of these fields. These function can either return an object or a promise. Once this promise is resolved, the children resolvers will take over and continue executing the operation.
Resolver Type Signature
Let's write a simple resolver to understand how fields are mapped with the main Query
and what are the positional arguments of these functions:
javascriptconst resolvers = { Query: { restaurants (parent, args, context, info) { /* Returning data after querying from the MongoDB collection */ } } }
The restaurants
field gets resolved using the Query.restaurants
function in the above map. This is a dummy resolver for now and does not return anything. We'll integrate MongoDB as our data source in the next article and then implement all of our resolvers.
The positional parameters of the resolve function are:
parent
: As mentioned above, the resolver either returns data or a promise. The parent
argument contains the data returned from the parent resolver.
args
: We've already seen the use of args
in this article. The args
object is used for getting a specific set of data.
context
: This Object is shared by all the resolvers in a particular query. It contains the information related to the request state like Authentication.
info
: It is defined as GraphQLResolveInfo
type and contains information about the execution state of the query like fieldName
, fieldASTs
, path
and more.
Conclusion
This article is an overview of the Apollo GraphQL platform. We learned that:
- GraphQL is a specification and Facebook built
graphql
module as a reference implementation in JavaScript. The Apollo GraphQL platform is a complete implementation of the GraphQL and is extensively used by the community. - The Apollo Server can be integrated with the express framework as middleware and it takes in the schema and the resolvers. It uses the GraphQL Schema Definition Language to define types.
- The Apollo Client is one of the popular ways to integrate GraphQL on the client-side. It solves various tough problems like Data Fetching, State Management, Caching on the client-side. It helps to eliminate a lot of boilerplate code in Redux which was used for handling the state and dispatching actions.
- We learned how to write an Apollo server using the
apollo-server-express
module. We also implemented the type definitions of Entities that are used in the Food Ordering System. - The Query component on the client-side makes it convenient to handle state in React.
In the next article, we'll integrate MongoDB data source with our Apollo Server. We'll also implement the resolvers for various fields in the Food Ordering project and fetch data from the collections in MongoDB.