Join us from October 8-10 in New York City to learn the latest tips, trends, and news about GraphQL federation and API platform engineering.Join us for GraphQL Summit 2024 in NYC
Docs
Start for Free

Advanced HTTP networking

Take full network control with Apollo Link


The Apollo Link library gives you fine-grained control of HTTP requests that are sent by . You can also use it to replace Apollo Client's networking layer with something completely custom, such as a WebSocket transport or mocked server data.

When using , you define network behavior as a collection of link objects that execute in sequence to control the flow of data. By default, Apollo Client uses Apollo Link's HttpLink to send queries over HTTP.

Apollo Link includes installable, premade links that support a variety of use cases. You can also create your own custom links.

Customizing request logic

The following example demonstrates adding a custom link to Apollo Client. This link adds an Authorization header to every HTTP request before the HttpLink sends it:

import { ApolloClient, HttpLink, ApolloLink, InMemoryCache, concat } from '@apollo/client';
const httpLink = new HttpLink({ uri: '/graphql' });
const authMiddleware = new ApolloLink((operation, forward) => {
// add the authorization to the headers
operation.setContext(({ headers = {} }) => ({
headers: {
...headers,
authorization: localStorage.getItem('token') || null,
}
}));
return forward(operation);
})
const client = new ApolloClient({
cache: new InMemoryCache(),
link: concat(authMiddleware, httpLink),
});

This next example demonstrates providing multiple custom links in an array:

import { ApolloClient, HttpLink, ApolloLink, InMemoryCache, from } from '@apollo/client';
const httpLink = new HttpLink({ uri: '/graphql' });
const authMiddleware = new ApolloLink((operation, forward) => {
// add the authorization to the headers
operation.setContext(({ headers = {} }) => ({
headers: {
...headers,
authorization: localStorage.getItem('token') || null,
}
}));
return forward(operation);
})
const activityMiddleware = new ApolloLink((operation, forward) => {
// add the recent-activity custom header to the headers
operation.setContext(({ headers = {} }) => ({
headers: {
...headers,
'recent-activity': localStorage.getItem('lastOnlineTime') || null,
}
}));
return forward(operation);
})
const client = new ApolloClient({
cache: new InMemoryCache(),
link: from([
authMiddleware,
activityMiddleware,
httpLink
]),
});

In the example above, the authMiddleware link sets each request's Authorization header, and the activityMiddleware then sets each request's Recent-Activity header. Finally, the HttpLink sends the modified request.

Customizing response logic

You can use Apollo Link to customize Apollo Client's behavior whenever it receives a response from a request.

The following example demonstrates using @apollo/client/link/error to handle network errors that are included in a response:

import { ApolloClient, InMemoryCache, HttpLink } from '@apollo/client';
import { onError } from '@apollo/client/link/error';
import { logout } from './logout';
const httpLink = new HttpLink({ uri: '/graphql' });
const logoutLink = onError(({ networkError }) => {
if (networkError.statusCode === 401) logout();
})
const client = new ApolloClient({
cache: new InMemoryCache(),
link: logoutLink.concat(httpLink),
});

In this example, the user is logged out of the application if the server returns a 401 code (unauthorized).

Modifying response data

You can create a custom link that edits or removes from response.data. To do so, you call map on the result of the link's forward(operation) call. In the map function, make the desired changes to response.data and then return it:

import { ApolloClient, InMemoryCache, HttpLink, ApolloLink } from '@apollo/client';
const httpLink = new HttpLink({ uri: '/graphql' });
const formatDateLink = new ApolloLink((operation, forward) => {
return forward(operation).map(response => {
if (response.data.date) {
response.data.date = new Date(response.data.date);
}
return response;
});
});
const client = new ApolloClient({
cache: new InMemoryCache(),
link: formatDateLink.concat(httpLink),
});

In the above example, formatDateLink changes a date to a Javascript Date object at the top level of each response.

Note that forward(operation).map(func) doesn't support asynchronous execution of the func mapping function. If you need to make asynchronous modifications, use the asyncMap function from @apollo/client/utilities, like so:

import {
ApolloClient,
InMemoryCache,
HttpLink,
ApolloLink
} from "@apollo/client";
import { asyncMap } from "@apollo/client/utilities";
import { usdToEur } from './currency';
const httpLink = new HttpLink({ uri: '/graphql' });
const usdToEurLink = new ApolloLink((operation, forward) => {
return asyncMap(forward(operation), async (response) => {
let data = response.data;
if (data.price && data.currency === "USD") {
data.price = await usdToEur(data.price);
data.currency = "EUR";
}
return response;
});
});
const client = new ApolloClient({
cache: new InMemoryCache(),
link: usdToEurLink.concat(httpLink)
});

In the example above, usdToEurLink uses asyncMap to convert the response object's price field from USD to EUR using an external API.

While this technique can be useful for modifying existing fields (or adding additional objects to lists within data), adding new fields to data won't work in most cases, because the cannot be safely modified within the ApolloLink request pipeline.

Apollo Client uses HttpLink to send GraphQL to a server over HTTP. The link supports both POST and GET requests, and it can modify HTTP options on a per- basis. This comes in handy when implementing authentication, , dynamic URIs, and other granular updates.

If your client doesn't have complex HTTP requirements, you probably don't need to create a custom instance of HttpLink. For details, see Basic HTTP networking.

Usage

import { HttpLink } from "@apollo/client";
const link = new HttpLink({ uri: "/graphql" });

Constructor options

The HttpLink constructor accepts the following options:

OptionsDescription
uriA string endpoint or function that resolves to the GraphQL server you want to execute operations against. (default: /graphql)
includeExtensionsIf true, you can pass an extensions field to your GraphQL server. (default: false)
fetchA fetch-compatible API for making a request. See Providing a fetch replacement for certain environments.
headersAn object containing header names and values to include in each request.
preserveHeaderCaseIf true, header values retain their capitalization for non-http-spec-compliant servers. (default: false)
credentialsA string representing the credentials policy to use for the fetch call. (valid values: omit, include, same-origin)
fetchOptionsInclude this to override the values of certain options that are provided to the fetch call.
useGETForQueriesIf true, HttpLink uses GET requests instead of POST requests to execute query operations (but not mutation operations). (default: false)
printA function to customize AST formatting in requests. See Overriding the default print function.

Providing a fetch replacement for certain environments

HttpLink requires that fetch is present in your runtime environment. This is the case for React Native and most modern browsers. If you're targeting an environment that doesn't include fetch (such as older browsers or the server), you need to pass your own fetch to HttpLink via its constructor options. We recommend using cross-fetch for older browsers and Node.

Overriding the default print function

The print option is useful for customizing the way DocumentNode objects are transformed back into strings before they are sent over the network. If no custom print function is provided, the GraphQL print function will be used. A custom print function should accept an ASTNode (typically a DocumentNode) and the original print function as , and return a string. This option can be used with stripIgnoredCharacters to remove whitespace from queries:

import { ASTNode, stripIgnoredCharacters } from 'graphql';
const httpLink = new HttpLink({
uri: '/graphql',
print(
ast: ASTNode,
originalPrint: (ast: ASTNode) => string,
) {
return stripIgnoredCharacters(originalPrint(ast));
},
});

Overriding options

You can override most HttpLink constructor options on an operation-by-operation basis by modifying the context object for the operation. For example, you can set the headers field on the context to pass custom headers for a particular operation. The context also supports the credentials field for defining credentials policy, uri for changing the endpoint dynamically, and fetchOptions to allow generic fetch overrides (i.e., method: "GET").

Note that if you set fetchOptions.method to GET, HttpLink follows the standard GraphQL HTTP GET encoding. The query, , , and are passed as query parameters instead of in the HTTP request body (because there isn't one). If you to continue to send as non-idempotent POST requests, set the top-level useGETForQueries option to true instead of setting fetchOptions.method to GET.

HttpLink also attaches the response from the fetch operation to the context as response, so you can access it in another link.

Context options:

OptionDescription
headersAn object containing header names and values to include in each request.
credentialsA string representing the credentials policy to use for the fetch call. (valid values: omit, include, same-origin)
uriA string endpoint or function that resolves to the GraphQL server you want to execute operations against.
fetchOptionsAny overrides of the fetch options argument to pass to the fetch call.
responseThe raw response from the fetch request after it is made.
httpAn object that lets you control fine-grained aspects of HttpLink itself, such as persisted queries (see below).

The following example shows how to use the context to pass a special header for a single query:

import { ApolloClient, InMemoryCache } from "@apollo/client";
const client = new ApolloClient({
cache: new InMemoryCache(),
uri: "/graphql"
});
client.query({
query: MY_QUERY,
context: {
// example of setting the headers with context per operation
headers: {
special: "Special header value"
}
}
});

Custom fetching

HttpLink's fetch option can be used to wire in custom networking. This is useful if you want to modify the request based on calculated headers, or calculate the URI based on an operation. For example:

Custom auth:

const customFetch = (uri, options) => {
const { header } = Hawk.client.header(
"http://example.com:8000/resource/1?b=1&a=2",
"POST",
{ credentials: credentials, ext: "some-app-data" }
);
options.headers.Authorization = header;
return fetch(uri, options);
};
const link = new HttpLink({ fetch: customFetch });

Dynamic URI:

const customFetch = (uri, options) => {
const { operationName } = JSON.parse(options.body);
return fetch(`${uri}/graph/graphql?opname=${operationName}`, options);
};
const link = new HttpLink({ fetch: customFetch });

Apollo Link includes many links for specialized use cases, such as the WebSocketLink for communicating over WebSocket and the BatchHttpLink for combining multiple GraphQL operations in a single HTTP request.

To learn more about what's available, see the Apollo Link API documentation.

Previous
Basic HTTP networking
Next
Authentication
Rate articleRateEdit on GitHubEditForumsDiscord

© 2024 Apollo Graph Inc., d/b/a Apollo GraphQL.

Privacy Policy

Company