You're viewing documentation for a version of this software that is in development. Switch to the latest stable version
/
Launch Graph Manager

Apollo Link overview

Customize Apollo Client's data flow


If your application only needs to send conventional HTTP-based requests to a GraphQL server, you probably don't need to use the Apollo Link API. To learn more, see Basic HTTP networking.

The Apollo Link library helps you customize the flow of data between Apollo Client and your GraphQL server. You can define your client's network behavior as a list of link objects that execute in a sequence:

Visualization of link interaction

In the above diagram:

  1. The first link might log the details of the operation for debugging purposes.
  2. The second link might add an HTTP header to the outgoing operation request.
  3. The third link might then send the request to a GraphQL server over HTTP.

Note that although the figure above shows the rightmost link requesting results from a remote server, it can execute GraphQL operations against any local or remote target that can respond to them.

By default, Apollo Client uses Apollo Link's HttpLink to send GraphQL operations to a remote server over HTTP. Apollo Client takes care of creating this default link, and it covers many use cases without requiring additional customization.

To extend or replace Apollo Client's default networking layer, you can define one or more custom links and specify their order of execution in the ApolloClient constructor.

A link can be either an instance of the ApolloLink class or a subclass of it. Regardless, it must define a method named request that:

  • Accepts an Operation object and a forward function
  • Returns an Observable, usually by calling the forward function

This request method is known as the link's request handler.

Here's an example of a custom link that defines its request handler by passing it as a parameter to the ApolloLink constructor:

import { ApolloLink } from '@apollo/client';

const timeStartLink = new ApolloLink((operation, forward) => {
  operation.setContext({ start: new Date() });
  return forward(operation);
});

Apollo Link uses the Observables implementation provided by zen-observable. Refer to the zen-observable documentation for additional Observable API details.

The request handler

Every link defines a request method, also known as its request handler. This method takes the following arguments:

  • operation: The GraphQL operation that's being passed through the link. For details, see The Operation object.
  • forward: A function for executing the next link in the chain (unless this is a terminating link).

Whenever Apollo Client prepares to execute a GraphQL operation, it calls the request handler on the first link in the chain. It's the responsibility of each link to perform its intended operation and then pass execution along to the next link in the chain by calling the forward function.

The Operation object

The Operation object includes the following fields:

NameDescription
queryA DocumentNode (parsed GraphQL operation) that describes the operation taking place.
variablesA map of GraphQL variables being sent with the operation.
operationNameA string name of the query if it is named, otherwise null.
extensionsA map to store extensions data to be sent to the server.
getContextA function to return the context of the request. This context can be used by links to determine which actions to perform. See Managing context.
setContextA function that takes either a new context object, or a function which takes in the previous context and returns a new one. See Managing context.
toKeyA function to convert the current operation into a string to be used as a unique identifier.

The forward function

When a link's request handler is done executing its logic, it should return a call to the forward function that's passed to it (unless it's the chain's terminating link). Calling the forward function passes execution along to the next link in the chain.

You can use Apollo Link helper functions from the @apollo/client package to compose your links. These functions are members of the ApolloLink class itself, and are explained in further detail in Composing a link chain.

Each link should represent a self-contained modification to a GraphQL operation. By composing these links into a chain, you can create an arbitrarily complex model for your client's data flow.

There are two forms of link composition: additive and directional.

  • Additive composition involves combining a set of links into a serially executed chain.
  • Directional composition involves branching to one of multiple links, depending on the details of an operation.

Note that no matter how you structure your links, the terminating link must be last.

The terminating link is the last link in your composed chain. Instead of calling the forward function, it's responsible for sending your composed GraphQL operation to the destination that will execute it (usually a GraphQL server) and returning an ExecutionResult.

Additive composition

If you have a collection of two or more links that should always be executed in the exact same order, you can use the ApolloLink.from helper method to combine those links into a single link, like so:

import { from, HttpLink } from '@apollo/client';
import { RetryLink } from '@apollo/link-retry';
import MyAuthLink from '../auth';

const link = from([
  new RetryLink(),
  new MyAuthLink(),
  new HttpLink({ uri: 'http://localhost:4000/graphql' })
]);

Directional composition

You might want your link chain's execution to branch, depending on the details of the operation being performed. You can define this logic with the split method of an ApolloLink instance. This method takes three parameters:

NameDescription
testA function that takes in the current Operation and returns either true or false depending on its details.
leftThe link to execute next if the test function returns true.
rightAn optional link to execute next if the test function returns false. If this is not provided, the link's forward parameter is used.

In the following example, a RetryLink passes execution along to one of two different HttpLinks depending on the associated context's version:

import { ApolloLink, HttpLink } from '@apollo/client';
import { RetryLink } from '@apollo/link-retry';

const link = new RetryLink().split(
  (operation) => operation.getContext().version === 1,
  new HttpLink({ uri: "http://localhost:4000/v1/graphql" }),
  new HttpLink({ uri: "http://localhost:4000/v2/graphql" })
);

Other uses for the split method include:

  • Customizing the number of allowed retry attempts depending on the operation type
  • Using different transport methods depending on the operation type (such as HTTP for queries and WebSocket for subscriptions)
  • Customizing logic depending on whether a user is logged in

Most links perform the same logic for every operation they process, and they don't need to know anything about operations that have been executed previously. These links are stateless.

You can define the request handler for a stateless link in the constructor of an ApolloLink object, like so:

import { ApolloLink } from '@apollo/client';

const consoleLink = new ApolloLink((operation, forward) => {
  console.log(`starting request for ${operation.operationName}`);
  return forward(operation).map((data) => {
    console.log(`ending request for ${operation.operationName}`);
    return data;
  })
})

Stateless links are great for implementing middleware and even network requests. The following link adds an Authorization header to every outgoing request:

import { ApolloLink } from '@apollo/client';

const authLink = new ApolloLink((operation, forward) => {
  operation.setContext(({ headers }) => ({ headers: {
    authorization: Auth.userId(), // however you get your token
    ...headers
  }}));
  return forward(operation);
});

This style of link also composes well for customization using a function:

import { ApolloLink } from '@apollo/client';

const reportErrors = (errorCallback) => new ApolloLink((operation, forward) => {
  const observer = forward(operation);
  // errors will be sent to the errorCallback
  observer.subscribe({ error: errorCallback })
  return observer;
});

const link = reportErrors(console.error);

You can also create a stateless link by extending the ApolloLink class and overwriting its constructor and request handler. For example, here's the same reportErrors link written as an extension of ApolloLink:

import { ApolloLink } from '@apollo/client';

class ReportErrorLink extends ApolloLink {
  constructor(errorCallback) {
    super();
    this.errorCallback = errorCallback;
  }
  request(operation, forward) {
    const observer = forward(operation);
    // errors will be sent to the errorCallback
    observer.subscribe({ error: this.errorCallback })
    return observer;
  }
}

const link = new ReportErrorLink(console.error);

When it's useful, links can maintain state between operations. These links are stateful.

Stateful links are usually defined as subclasses of ApolloLink. They override the constructor of ApolloLink and implement a request function with the same signature as a stateless link. For example:

import { ApolloLink } from '@apollo/client';

class OperationCountLink extends ApolloLink {
  constructor() {
    super();
    this.operationCount = 0;
  }
  request(operation, forward) {
    this.operationCount += 1;
    return forward(operation);
  }
}

const link = new OperationCountLink();

This stateful link maintains a counter called operationCount as an instance variable. Every time a request is passed through the link, operationCount is incremented.

Managing context

As an operation moves down your link chain, it maintains a context that each link can read and modify. This allows links to pass metadata down the chain that other links use in their execution logic.

  • Obtain the current context object by calling operation.getContext().
  • Modify the context object and then write it back with operation.setContext(newContext) or operation.setContext((prevContext) => newContext).

Note that this context is not included with the operation in the terminating link's request to the GraphQL server or other destination.

Here's an example:

import { ApolloLink } from '@apollo/client';

const timeStartLink = new ApolloLink((operation, forward) => {
  operation.setContext({ start: new Date() });
  return forward(operation);
});

const logTimeLink = new ApolloLink((operation, forward) => {
  return forward(operation).map((data) => {
    // data from a previous link
    const time = new Date() - operation.getContext().start;
    console.log(`operation ${operation.operationName} took ${time} to complete`);
    return data;
  })
});

const link = timeStartLink.concat(logTimeLink)

This example defines two links, timeStartLink and logTimeLink. The timeStartLink assigns the current time to the context's start field. When the operation completes, the logTimeLink then subtracts the value of start from the current time to determine the total duration of the operation.

The context's initial value can be set by Apollo Client before the link chain begins its execution. In this example, a call to client.query adds a saveOffline field to the context, which is then read by the custom link defined at the top:

import { ApolloLink } from '@apollo/client';

const link = new ApolloLink((operation, forward) => {
  const { saveOffline } = operation.getContext();
  if (saveOffline) // do offline stuff
  return forward(operation);
})

const client = new ApolloClient({
  cache: new InMemoryCache()
  link,
});

// send context to the link
const query = client.query({ query: MY_GRAPHQL_QUERY, context: { saveOffline: true }});
Edit on GitHub