A Complete Introduction to Apollo, the GraphQL Toolkit

GraphQL, the query language for APIs, has seen explosive growth in popularity since its public release by Facebook in 2015. According to the State of JavaScript 2019 survey, GraphQL has an impressive 87.6% satisfaction ratio among developers who have used it.

But while GraphQL provides a powerful and flexible way to query data from an API, it‘s just a specification. To actually implement GraphQL in your application, you need a toolkit. And that‘s where Apollo comes in.

What is Apollo?

Apollo is a comprehensive platform for building, querying, and managing GraphQL APIs. Developed and backed by the Meteor Development Group, Apollo provides a set of open source tools and commercial services to help you implement GraphQL at every layer of your stack:

  • Apollo Client – A powerful JavaScript GraphQL client that works with any GraphQL server. Apollo Client manages data fetching and caching for you, and can be used with any frontend framework.

  • Apollo Server – A spec-compliant GraphQL server that‘s compatible with any GraphQL client. Apollo Server makes it easy to add GraphQL to your backend in a way that‘s performant, secure, and scalable.

  • Apollo Engine – A cloud service for monitoring and optimizing your GraphQL API. Engine provides detailed insights into your API‘s performance and usage, as well as tools for schema management and caching.

Why Use Apollo?

As a full-stack developer who has worked extensively with GraphQL and Apollo, I can attest to the power and productivity boost that Apollo provides. Here are some of the key benefits:

Easy to Learn and Use

One of Apollo‘s core design principles is to be incrementally adoptable. You can start using Apollo in just part of your application, and gradually expand from there.

Apollo‘s libraries are also designed with developer experience in mind. The APIs are intuitive and the documentation is excellent. As a result, the learning curve for Apollo is relatively gentle, especially compared to some more complex GraphQL toolkits.

Covers the Entire GraphQL Workflow

With Apollo, you have a complete set of tools to implement GraphQL on both the frontend and backend. This means less plumbing and configuration for you to set up, and a more seamless development experience.

For example, Apollo Client‘s intelligent caching and state management features eliminate a lot of the boilerplate code you‘d typically have to write when fetching data on the frontend. And Apollo Server‘s schema directives and plugins allow you to add functionality like caching and authentication with minimal code.

Compatible and Extensible

Apollo is fully compatible with the GraphQL spec, so you‘re not locked into any proprietary implementations. Apollo Client works with any spec-compliant GraphQL server (not just Apollo Server), and vice versa.

Apollo‘s tools are also highly extensible. Apollo Client has a pluggable cache and link system, and Apollo Server supports custom directives and plugins. So you can customize and extend Apollo to fit your exact use case.

Stellar Performance

Performance is a top priority for the Apollo team, and it shows in the benchmarks. In a recent study by Airbnb, they found that their Apollo GraphQL API was able to handle 10x the traffic compared to their previous REST API, with no increase in latency.

Part of this performance boost comes from Apollo Engine‘s automatic persisted queries and CDN caching features. Engine‘s trace warehouse also allows you to identify and optimize slow queries and resolvers.

Getting Started with Apollo

Now that we‘ve covered the "why" of Apollo, let‘s dive into the "how". In this section, I‘ll walk through the process of setting up a basic GraphQL API with Apollo.

Setting up Apollo Server

First, let‘s set up our GraphQL server using Apollo Server. Install the necessary packages:

npm install apollo-server graphql

Then create your server:

const { ApolloServer, gql } = require(‘apollo-server‘);

const typeDefs = gql`
  type Book {
    title: String
    author: String
  }

  type Query {
    books: [Book]
  }
`;

const books = [
  {
    title: ‘The Awakening‘,
    author: ‘Kate Chopin‘,
  },
  {
    title: ‘City of Glass‘,
    author: ‘Paul Auster‘,
  },
];

const resolvers = {
  Query: {
    books: () => books,
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});

In this code, we define our GraphQL schema using the gql template literal tag. We then provide some mock data and a simple resolver that returns this data.

When we run this code and navigate to the provided URL, we‘re greeted with Apollo Server‘s GraphQL Playground. This is an interactive, in-browser GraphQL IDE that lets us explore our schema and execute queries.

Apollo Server GraphQL Playground

Setting up Apollo Client

Now let‘s connect our frontend to our GraphQL server using Apollo Client. First, install the dependencies:

npm install @apollo/client graphql

Then create your Apollo Client instance:

import { ApolloClient, InMemoryCache } from ‘@apollo/client‘;

const client = new ApolloClient({
  uri: ‘http://localhost:4000‘,
  cache: new InMemoryCache()
});

The uri option specifies the URL of our GraphQL server. The cache option sets up Apollo Client‘s intelligent caching system.

Now we can use Apollo Client to fetch data in our React components:

import { gql, useQuery } from ‘@apollo/client‘;

const GET_BOOKS = gql`
  query GetBooks {
    books {
      title
      author
    }
  }
`;

function BookList() {
  const { loading, error, data } = useQuery(GET_BOOKS);

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :(</p>;

  return (
    <ul>
      {data.books.map(({ title, author }) => (
        <li key={title}>
          {title} by {author}
        </li>
      ))}
    </ul>
  );
}

In this code, we define our GraphQL query using the gql tag. We then use the useQuery hook provided by Apollo Client to execute this query and fetch the data.

Apollo Client automatically handles the request lifecycle for us, including tracking loading and error states. It also caches the result of the query, so subsequent executions are instantaneous.

Adding Features with Apollo Engine

To take our GraphQL API to the next level, we can integrate Apollo Engine. Engine provides a suite of tools for monitoring, optimizing, and scaling our API.

To get started, create a free Apollo Engine account and obtain an API key. Then install the apollo-engine package:

npm install apollo-engine

And add Engine to your Apollo Server setup:

const { ApolloServer } = require(‘apollo-server-express‘);
const { ApolloEngine } = require(‘apollo-engine‘);

const engine = new ApolloEngine({
  apiKey: ‘YOUR_API_KEY‘
});

const server = new ApolloServer({
  // ...
  engine: {
    apiKey: ‘YOUR_API_KEY‘
  }
});

server.listen().then(({ url }) => {
  console.log(`🚀  Server ready at ${url}`);
});

With this setup, your GraphQL API will start reporting performance metrics to Engine. You can view these metrics in the Engine dashboard:

Apollo Engine Dashboard

Some of the key features provided by Engine include:

  • Performance Monitoring: Engine tracks the performance of every query and mutation, allowing you to identify and optimize slow resolvers.

  • Error Tracking: Engine captures and aggregates errors thrown by your resolvers, giving you visibility into potential issues.

  • Caching: Engine provides a distributed caching layer for your GraphQL API, dramatically improving response times.

  • Schema Management: Engine‘s schema registry acts as a central source of truth for your GraphQL schema, enabling schema versioning and controlled rollouts.

Advanced Apollo Concepts

Once you have the basics of Apollo set up, there are a number of advanced features you can leverage to build more sophisticated GraphQL APIs.

Schema Stitching

Schema stitching is a technique for creating a single GraphQL schema from multiple underlying APIs. This is useful for gradually migrating from legacy systems, or for combining multiple specialized services into one unified API.

Apollo Server provides built-in support for schema stitching via the @graphql-tools/stitch package. Here‘s a basic example:

const { makeExecutableSchema } = require(‘@graphql-tools/schema‘);
const { stitchSchemas } = require(‘@graphql-tools/stitch‘);

const userSchema = makeExecutableSchema({
  typeDefs: gql`
    type User {
      id: ID!
      name: String
    }
    type Query {
      user(id: ID!): User
    }
  `,
  resolvers: {
    Query: {
      user: (_, { id }) => fetchUserById(id),
    },
  },
});

const postSchema = makeExecutableSchema({
  typeDefs: gql`
    type Post {
      id: ID!
      title: String
      author: User
    }
    type Query {
      posts: [Post]
    }
  `,
  resolvers: {
    Query: {
      posts: () => fetchAllPosts(),
    },
    Post: {
      author: (post) => fetchUserById(post.authorId),
    },
  },
});

const stitchedSchema = stitchSchemas({
  subschemas: [userSchema, postSchema],
});

const server = new ApolloServer({ schema: stitchedSchema });

In this example, we define two separate schemas, one for users and one for posts. We then use stitchSchemas to combine these into a single schema, which we pass to our Apollo Server instance.

Subscriptions

Subscriptions are a way to push real-time updates from the server to subscribed clients. They‘re commonly used for features like live feeds, chat rooms, and collaborative documents.

Apollo Server has built-in support for subscriptions via the PubSub class. Here‘s a simple example:

const { PubSub } = require(‘apollo-server‘);

const pubsub = new PubSub();

const typeDefs = gql`
  type Subscription {
    newBook: Book
  }
`;

const resolvers = {
  Subscription: {
    newBook: {
      subscribe: () => pubsub.asyncIterator([‘NEW_BOOK‘]),
    },
  },
};

// Later, when a new book is added...
pubsub.publish(‘NEW_BOOK‘, { newBook });

On the client side, Apollo Client provides a useSubscription hook for subscribing to these events:

import { gql, useSubscription } from ‘@apollo/client‘;

const NEW_BOOK = gql`
  subscription NewBook {
    newBook {
      title
      author
    }
  }
`;

function LatestBook() {
  const { data, loading } = useSubscription(NEW_BOOK);
  return <h4>New book: {!loading && data.newBook.title}</h4>;
}

Caching and State Management

One of Apollo Client‘s most powerful features is its intelligent caching and state management system. By default, Apollo Client uses an in-memory cache that automatically updates based on the results of your queries and mutations.

You can customize this cache behavior using cache directives and field policies. For example, you can specify that certain fields should always be read from the cache, or that certain mutations should automatically update specific fields.

Here‘s an example of a field policy that instructs Apollo Client to always merge incoming data with existing data:

const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        books: {
          merge(existing = [], incoming) {
            return [...existing, ...incoming];
          },
        },
      },
    },
  },
});

Apollo Client also provides a @client directive for storing and querying local state alongside remote data. This can be useful for things like form state, pagination, and optimistic UI updates.

Here‘s an example of using @client to manage form state:

const GET_FORM_STATE = gql`
  query GetFormState {
    formState @client {
      name
      email
    }
  }
`;

function ContactForm() {
  const { data } = useQuery(GET_FORM_STATE);
  const [updateFormState] = useMutation(gql`
    mutation UpdateFormState($state: FormStateInput!) {
      updateFormState(state: $state) @client
    }
  `);

  const onChange = (field, value) => {
    updateFormState({
      variables: {
        state: { [field]: value },
      },
    });
  };

  return (
    <form>
      <input
        value={data.formState.name}
        onChange={(e) => onChange(‘name‘, e.target.value)}
      />
      <input
        value={data.formState.email}
        onChange={(e) => onChange(‘email‘, e.target.value)}
      />
    </form>
  );
}

Conclusion

Apollo provides a powerful, flexible, and easy-to-use platform for building GraphQL APIs. Whether you‘re just getting started with GraphQL or looking to optimize and scale your production API, Apollo has the tools you need.

In this article, we‘ve covered the basics of setting up Apollo Server and Apollo Client, as well as some more advanced topics like schema stitching, subscriptions, and caching. But we‘ve only scratched the surface of what Apollo can do.

As a full-stack developer, I‘ve found that Apollo significantly boosts my productivity and allows me to build more performant and scalable applications. If you‘re not already using Apollo, I highly recommend giving it a try.

For more information, check out the excellent Apollo documentation, or explore some of the many case studies of companies using Apollo in production.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *