Intro to GraphQL with Apollo and React

List Of Content

ADS Area (CARBON)

ADS Area (CARBON)

 

You can grab Source codes:

Server Repo 

Client Repo

GraphQL 

GraphQL is an open-source Data and Manipulation language developed and used internally at facebook since 2012. It introduces a new revolutionary API to fetch data between a server and a client clearly and simply with reliability and ease to use.

GraphQL uses and Advanced typing system for defining data on the server and for client queries.

Check more at GraphQL.org

Apollo

Apollo is a complete platform for implementing a graph over your data. It includes two runtime libraries, Apollo Server and Apollo Client, for building and querying your graph's API. It also features developer tooling that integrates with your existing workflow and gives you full visibility into the performance and security of your graph.

The reason we chose to use Apollo because it implements most of the complete features of GraphQL Specs which has two runtimes:

  • Apollo Server: For Server Implementation which supports many frameworks.
  • Apollo Client: For Client side implementation supports (Angular, Vanilla, React...).

We will be using Apollo packages to integrate GraphQL with our Basic Nodejs/Express server and implement our API.

REST Vs. GraphQL

Representational State Transfer is an API that allows for data transfer over HTTP Protocol mostly used for Web Application to implement their service API and to allow data fetching, submitting and removal for the CRUD implementation.

REST is revolutionary and still used by large companies and teams to implement their APIs but it has some drawbacks:

  • Over-fetching
    • /products?field=name&field=description&field=variants[*].price
  • Under-fetching
    • /products?expand=productType&expand=variants[*].price.taxRate
  • API changes and evolution
    • Versioning
    • Deprecation
    • Maintenance

GraphQL is a data language used to develop APIs it comes as a better alternative for API implementation since it uses a schema definition language SDL for keeping track of your API implementation and consuming strategy.

  • A data query language
  • Developed by Facebook
  • Used internally since 2012
  • The open-source version published in July 2015
  • Relay released in August 2015
  • Specification: https://facebook.github.io/graphql

Once you start using GraphQL you will find a lot of hidden reason why it gives you much power to do your APIs the right way.

REST is still very common though and still a pretty great choice under some circumstances.

Apollo Server (Implementing GraphQL-Server)

The reason we chose Apollo is that it gives us a ready implementation for our server-side and client-side we will be using apollo-server-express which implements a middleware for bootstrapping and configuring your GraphQL API.

I assume you already have a basic Nodejs/Express server running (for ex: PORT 4000).

Let's install Apollo and GraphQL:

npm install graphql apollo-server-express --save

You may also need to install the cors package for allowing CORS (Cross-Origin Resource Sharing) for allowing request from a different domain (only for development).

npm install cors --save

You simply need to implement the middleware that hooks up GraphQL with your express server, of course, you need to provide two types of objects for GraphQL endpoint:

  • TypeDefs: your Types Schema which defines the main Query and your data types.
  • Resolvers: resolvers map is what maps each query endpoint to a function that runs once the query is sent.

Let's define a simple Schema that allows us to query for books on the store as well as retrieving a single book by name.

/* schema.js */

const { gql } = require("apollo-server-express");
//Using template string literals 
module.exports = gql`
  type Author {
    name: String!
  }

  type Book {
    name: String!
    author: String!
    numPages: Int!
  }

  type Query {
    hello: String!
    books: [Book]
    book(name: String!): Book
  }
`;

We got an Author type that defines its name attribute, Book type define name (required), author (required) and numPages(required) the exclamation mark at the end means the current field is required and cannot be nullable. 

The Query type is the main entry point for our GraphQL API which means they play functions roles which allow us to call it from the client-side, only the Query object is accessible from the client-side everything else is stored on your server to execute the query. 

Learn SDL.

The query endpoints need to be implemented which means we need to write a resolver for each query to handle the query and return the desired type.

Let's create our resolvers map.

const { Books } = require("./constants");

module.exports = {
  Query: {
    hello() {
      return "Hello World from the Other Side!";
    },

    books() {
      return Books;
    },

    book(root, args, context) {
      const { name } = args;
      if (!name || name === "") return null;
      const foundBook = Books.find(book => book.name === name);
      if (!foundBook) return null;
      return foundBook;
    }
  }
};

The resolvers map is an Object wrapped with a query object cause there other types in GraphQL other than query. it holds the endpoint fields as a function to be run once the query runs.

hello() simply returns a string since we expected a required string in the schema.

books() we return a hardcoded array of books (basic array of objects) in real-word scenario it would be a database system you use to get the list of available books.

book() takes some arguments and they exist by default on every apollo resolver function (the function does find a book by name and returns the book or null).

  • root: is an object that holds the return data from the parent query in case if the current resolver is a nested child.
  • args: query arguments that are sent by the client (key-value pairs), remember you can't send args other than the ones are specified on the schema.   
  • context: is a shared object instance among all resolvers so you can share data between one resolver and another (a query depends on other queries for ex: Authentication).

Let's test our server using Apollo-playground which is an API testing tool developed by Prisma. Open up http://localhost:4000/graphql

The response is an object with a data field that holds your queried data.

You can also right queries for books and test using the playground before moving to your client-side implementation.

Apollo Client (Implementing React Application)

Apollo works pretty well with react, so we will be creating a simple react application to fetch the store books through the GraphQL API server.

I created and bootstrapped react project using create-react-app.

Let's install the necessary apollo and Graphql packages for our React app.

npm install apollo-boost react-apollo graphql --save

You may also need to use styled-components for working with CSS-in-JS.

npm install styled-components --save

Since we will be fetching books and rendering them we need to create a custom Books component that takes care of the rendering side.

/* components/books.jsx */
import React from "react";
//We use styled compoents to write CSS in JS for our React Components.
import styled from "styled-components/macro";

const BooksContainer = styled.div`
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
`;

const Book = styled.div`
  display: flex;
  flex-direction: column;
  min-height: 5em;
  padding: 25px;
  border: 1px solid rgba(15, 15, 15, 0.2);
  margin-right: 25px;
  border-radius: 4px;
`;

const BookTitle = styled.div`
  font-size: 20px;
  margin-bottom: 1em;
  color: #3d3d3d;
  font-weight: 700;
`;

const AuthorName = styled.div`
  font-size: 17px;
`;

const NumPages = styled.div`
  color: rgba(15, 15, 15, 0.5);
  font-size: 14px;
`;

export function Books(props) {
  const { books } = props;

  //NOTE: loop through the books array received through props and render each one.

  return (
    <BooksContainer>
      {books.map((book, idx) => {
        return (
          <Book key={`${book.name}-${idx}`}>
            <BookTitle>{book.name}</BookTitle>
            <AuthorName>{book.author}</AuthorName>
            <NumPages>{book.NumPages}</NumPages>
          </Book>
        );
      })}
    </BooksContainer>
  );
}

The Books component need books props in order to render each book in the list, so we need to fetch the list from our GraphQL API using Apollo Query Component.

/* App.jsx */ 
import React from "react";
import "./App.css";
import ApolloClient, { gql } from "apollo-boost";
import { ApolloProvider, Query } from "react-apollo";
import { Books } from "./components/books";

//We write our GraphQL Query 
const GET_BOOKS = gql`
  query getBooks {
    myBooks: books {
      name
      author
      numPages
    }
  }
`;

 GraphQL Query has to be written in gql template literal parser to get processed and sent over the network.

We first write the query name getBooks and inside of it we right wich resource(endpoint) we'are querying in this case books we'are also using an alias to create a field containing all the fetched books named myBooks instead of books.

Inside the books query object, we decide which fields we want to fetch, notice that you have to explicitly tell which fields otherwise they won't be fetched.

Now we need to run the query inside of an Apollo provider context in order to access the client instance.

function App() {
  //Create ApolloClient instance 
  //Provide your GraphQL API Endpoint
  const client = new ApolloClient({ uri: "http://localhost:4000/graphql" });
  
  /* ApolloProvider uses the context API, it has to take the client instance 
     and then provide it to all children through ApolloConsumer  
  */

  /* Query is imported from react-apollo which takes care of running the GET_BOOKS query 
     and provides fetched data through render-props.
  */  

  return (
    <ApolloProvider client={client}>
      <div className="App">
        <h3>Store Books</h3>
        <Query query={GET_BOOKS}>
          {props => {
            console.log("Props: ", props);
            const { error, loading, data } = props;
            if (error) return <div>Error: {error.message}</div>;
            if (loading) return <div>Loading...</div>;
            if (!data && !data.myBooks) return <div>No Books Found!</div>;
            return <Books books={data.myBooks} />;
          }}
        </Query>
      </div>
    </ApolloProvider>
  );
}

export default App;

ApolloProvider uses the React Context API to feed all its children with client prop to make GraphQL Query request to the initialized Endpoint.

The Query component takes your GraphQL Query and executes it, then provides the fetched data (Asynchronously) through the render-props React API which gives us a callback function with the desired data, error and loading props.

Make sure to check for errors and loading before rendering the desired element with data, you also need to make sure the requested data is valid before accessing.

Open up React app you should see the list of books.

Share Tutorial

Made With By

Ipenywis Founder, Game/Web Developer, Love Play Games