Concepts Overview
What you need to know to create your own Links.
Apollo Link is designed to be a powerful way to compose actions around data handling with GraphQL. Each link represents a subset of functionality that can be composed with other links to create complex control flows of data.
At a basic level, a link is a function that takes an operation and returns an observable. An operation is an object with the following information:
query
: ADocumentNode
(parsed GraphQL Operation) describing the operation taking placevariables
: A map of variables being sent with the operationoperationName
: A string name of the query if it is named, otherwise it is nullextensions
: A map to store extensions data to be sent to the servergetContext
: A function to return the context of the request. This context can be used by links to determine which actions to perform.setContext
: A function that takes either a new context object, or a function which receives the previous context and returns a new one. It behaves similarly tosetState
from React.toKey
: A function to convert the current operation into a string to be used as a unique identifier
We can chain these links together so that the first link operates on an operation object and each subsequent link operates on the result of the previous link. This allows us to "compose" actions and implement complex data handling logic in an elegant manner. We can visualize them like this:
Note that although we have the terminating link requesting GraphQL results from a server in this figure, this doesn't necessarily have to be the case: your GraphQL results can come from anywhere. For example, apollo-link-state
allows to use GraphQL operations to query client state.
Request
At the core of a link is the request
method. It takes the following arguments:
operation
: The operation being passed through the link.forward
: (optional) Specifies the next link in the chain of links.
A link's request
method is called every time execute
is run on that link chain, which typically occurs for every operation passed through the link chain. When the request
method is called, the link "receives" an operation and has to return back data of some kind in the form of an Observable
. Depending on where the link is in the chain (i.e. whether or not it is at the end of the chain), it will either use the forward
, the second parameter specifying the next link in the chain, or return back an ExecutionResult
on its own.
The full description of a link's request looks like this:
NextLink
: A function that takes anOperation
and returns an Observable of anExecutionResult
RequestHandler
: A function which receives anOperation
and aNextLink
and returns an Observable of anExecutionResult
As you can see from these types, the next link is a way to continue the chain of events until data is fetched from some data source (typically a server).
Terminating Links
Since link chains have to fetch data at some point, they have the concept of a terminating
link and non-terminating
links. Simply enough, the terminating
link is the one that doesn't use the forward
argument, but instead turns the operation into the result directly. Typically, this is done with a network request, but there are endless ways of delivering an ExecutionResult.
The terminating link is the last link in the composed chain.
Composition
Links are designed to be composed together to form control flow chains to manage a GraphQL operation request. They can be used as middleware to perform side effects, modify the operation, or even just provide developer tools like logging. They can be afterware which process the result of an operation, handle errors, or even save the data to multiple locations. Links can make network requests including HTTP, WebSockets, and even across the react-native bridge to the native thread for resolution of some kind.
When writing a RequestHandler
, the second argument is the way to call the next link in the chain. We refer to this argument as forward
in the docs for a couple of reasons. First, observers
have a next
function for sending new results to the subscriber. Second, if you think of composed links like a chain, the request goes forward
until it gets data (for example from a server request), then it begins to go back
up the chain to any subscriptions. The forward
function allows the RequestHandler
to continue the process to the next link in the chain.
The helper functions exported from the apollo-link
package can be used to perform composition of links. These functions are also conveniently located on the ApolloLink
class itself. They are explained in further detail here.
Context
Since links are meant to be composed, they need an easy way to send metadata about the request down the chain of links. They also need a way for the operation to send specific information to a link no matter where it was added to the chain. To accomplish this, each Operation
has a context
object which can be set from the operation while being written and read by each link. The context is read by using operation.getContext()
and written using operation.setContext(newContext)
or operation.setContext((prevContext) => newContext)
. The context
is not sent to the server, but is used for link to link communication. The API of setContext
is meant to be similar to React's setState
. For example:
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)
Each context can be set by the operation it was started on. For example with 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 }});