Migrating from 1.0

Updating your app to Apollo Client 2.0

Why the 2.0


The 2.0 version of Apollo provides a more customizable experience with GraphQL. It prioritizes features like custom execution chains (using Apollo Link) and custom stores while providing powerful defaults. It is an overall minor change to the API so you shouldn’t have to change very much code in your current app at all!

Goals


The 2.* version of Apollo Client builds on the original principles of the project. For reference, those goals are:

  1. Incrementally adoptable, so that you can drop it into an existing JavaScript app and start using GraphQL for just part of your UI.
  2. Universally compatible, so that Apollo works with any build setup, any GraphQL server, and any GraphQL schema.
  3. Simple to get started with, you can start loading data right away and learn about advanced features later.
  4. Inspectable and understandable, so that you can have great developer tools to understand exactly what is happening in your app.
  5. Built for interactive apps, so your users can make changes and see them reflected in the UI immediately.
  6. Small and flexible, so you don’t get stuff you don’t need
  7. Community driven, Apollo is driven by the community and serves a variety of use cases. Everything is planned and developed in the open.

Based on feedback from a wide variety of users, the 2.* version doubles down on being incrementally adoptable and flexible by allowing much stronger extension points. Customization of the client (i.e. data store, execution chain, etc) is a primary feature in the revised API. This version also take steps to reduce the overall size of the default client by 200% and provide the foundations for Apollo powering more of the application experience from development to production with client side state management.

The goal of the 2.0 launch is not to provide all of the new features that have been asked to be built in. Instead, the 2.0 makes a few key changes to both management of the code base (lerna / small modules) and the changes necessary to support custom stores and links fully. Apollo Client 2.0 is the jumping off point for user-land driven innovation (custom stores, custom links) and internal refactor (moving query manager into links, breaking apart the store / links into packages, etc)

Installation


One of the largest changes with the new version is the breaking apart of the monolith apollo-client package into a few small, but isolated modules. This gives way more flexibility, but does require more packages to install. Long term we think this is the right way to go for applications of all sizes, but we have created an apollo-client-preset package with defaults that match the 1.0 version.

To get started with the 2.0, you will change your imports from either react-apollo, or just apollo-client to use the new packages. A typical upgrade looks like this:

Before

1
2
3
4
5
6
7
8
9
10
import {
ApolloClient,
createNetworkInterface,
ApolloProvider,
graphql,
gql
} from 'react-apollo';

// or
import { ApolloClient, createNetworkInterface } from 'apollo-client';

After

1
2
3
4
5
import { ApolloClient } from 'apollo-client';
import { HttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import gql from 'graphql-tag';
import { ApolloProvider, graphql } from 'react-apollo';

By splitting these features apart, it makes it possible to import only what your application needs and it makes it possible for us to indvidually increment versions instead of where a breaking change in one, means a breaking change in all since the packages are re-exported;

The above example can be reduced using apollo-client-preset like so:

1
2
3
import { ApolloClient, HttpLink, InMemoryCache } from 'apollo-client-preset';
import gql from 'graphql-tag';
import { ApolloProvider, graphql } from 'react-apollo';

Basic updates


We know how much hard work and care you have put into your application! The 2.0 upgrade is designed to be as minimal as possible and to not require any application (like UI integration) changes. It is instead focused on how to construct a client as a way to new features and improvements. A simple usage of Apollo Client upgrading to the 2.0 would look like this:

Before

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import ApolloClient, { ApolloProvider, createNetworkInterface } from 'react-apollo';
import { render } from 'react-dom';

const client = new ApolloClient({
networkInterface: createNetworkInterface({ uri: 'http://localhost:3000' }),
});

const WrappedApp = (
<ApolloProvider client={client}>
<App />
</ApolloProvider>
);

render(WrappedApp, document.getElementById('app'));

After

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { ApolloProvider } from 'react-apollo';
import { render } from 'react-dom';

const client = new ApolloClient({
link: createHttpLink({ uri: 'http://localhost:3000' }),
cache: new InMemoryCache(),
});

const WrappedApp = (
<ApolloProvider client={client}>
<App />
</ApolloProvider>
);

render(WrappedApp, document.getElementById('app'));

Custom configuration


Since everything was baked into the 1.0, custom configuration of the parts, like the network interface or cache, all were done on the constructor. With the 2.0, this is broken up slightly, and uneccessary configurations were removed. The following code snippet shows every possible option with the previous version and how to use it with the 2.0:

Before

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import ApolloClient, { createNetworkInterface } from 'apollo-client';

const client = new ApolloClient({
networkInterface: createNetworkInterface({
uri: 'http://api.example.com/',
opts: {
// Additional fetch options like `credentials` or `headers`
credentials: 'same-origin',
}
}),
reduxRootSelector: ({ apollo }) => apollo,
initialState: window.__APOLLO_CLIENT__,
dataIdFromObject: () => // custom idGetter,
ssrMode: true,
ssrForceFetchDelay: 100,
addTypename: true,
customResolvers: {},
connectToDevTools: true,
queryDeduplication: true,
fragmentMatcher: new FragmentMatcher()
})

After

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import { ApolloClient } from 'apollo-client';
import { InMemoryCache, IntrospectionFragmentMatcher } from 'apollo-cache-inmemory';
import { createHttpLink } from 'apollo-link-http';

const link = createHttpLink({
uri: 'http://api.example.com/',
credentials: 'same-origin'
})

const cache = new InMemoryCache({
dataIdFromObject: () => // custom idGetter,
addTypename: true,
cacheResolvers: {},
fragmentMatcher: new IntrospectionFragmentMatcher({
introspectionQueryResultData: yourData
}),
});

const client = new ApolloClient({
link,
// use restore on the cache instead of initialState
cache: cache.restore(window.__APOLLO_CLIENT__),
ssrMode: true,
ssrForceFetchDelay: 100,
connectToDevTools: true,
queryDeduplication: true,
});

// reduxRootSelector has been removed

Note If you were using customResolvers, the name of that has been changed to be cacheResolvers to be more descriptive of what it does. customResolvers will still be supported throughout the 2.0 though to be backwards compatible and ease the upgrade path

Cache extraction


If you have previously used getInitialState for SSR, that API has been moved to the cache itself instead of on the client. You can read more about the recipe for server side rendering here. The upgrade path looks like this:

Before

1
2
3
4
5
6
7
import { ApolloClient } from 'apollo-client';

const client = new ApolloClient();

// do some data loading things using getDataFromTree

const state = client.getInitialState();

After

1
2
3
4
5
6
7
8
9
10
11
12
import { ApolloClient } from "apollo-client";
import { InMemoryCache } from "apollo-cache-inmemory";
import { createHttpLink } from 'apollo-link-http';

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

// do some data loading things using getDataFromTree

const state = client.cache.extract();

Network Middleware and Afterware


If you previously used use or useAfter on the networkInterface from the 1.0 of Apollo Client, you will need to update to use Apollo Links as the new way to handle *wares in the 2.0. Apollo Link provides a lot more power for *ware features and more information is available here. A few examples of migrating custom *ware methods to Apollo Links are shown below:

Middleware

Before

1
2
3
4
5
6
7
8
9
10
11
12
13
import { createNetworkInterface } from 'apollo-client';

const networkInterface = createNetworkInterface({ uri: '/graphql' });

networkInterface.use([{
applyMiddleware(req, next) {
if (!req.options.headers) {
req.options.headers = {}; // Create the header object if needed.
}
req.options.headers['authorization'] = localStorage.getItem('token') ? localStorage.getItem('token') : null;
next();
}
}]);

After

1
2
3
4
5
6
7
8
9
10
11
12
13
import { setContext } from 'apollo-link-context';
import { createHttpLink } from 'apollo-link-http';

const httpLink = createHttpLink({ uri: '/graphql' });

const middlewareLink = setContext(() => ({
headers: {
authorization: localStorage.getItem('token') || null,
}
}));

// use with apollo-client
const link = middlewareLink.concat(httpLink);

Afterware (error handling)

Before

1
2
3
4
5
6
7
8
9
10
11
12
13
import { createNetworkInterface } from 'apollo-client';
import { logout } from './logout';

const networkInterface = createNetworkInterface({ uri: '/graphql' });

networkInterface.useAfter([{
applyAfterware({ response }, next) {
if (response.status === 401) {
logout();
}
next();
}
}]);

After

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { createHttpLink } from 'apollo-link-http';
import { onError } from 'apollo-link-error';

import { logout } from './logout';

const httpLink = createHttpLink({ uri: '/graphql' });

const errorLink = onError(({ networkError, graphQLErrors }) => {
if (networkError.statusCode === 401) {
logout();
}
})

// use with apollo-client
const link = errorLink.concat(httpLink);

Afterware (data manipulation)

Before

1
2
3
4
5
6
7
8
9
10
11
12
import { createNetworkInterface } from 'apollo-client';

const networkInterface = createNetworkInterface({ uri: '/graphql' });

networkInterface.useAfter([{
applyAfterware({ response }, next) {
if (response.data.user.lastLoginDate) {
response.data.user.lastLoginDate = new Date(response.data.user.lastLoginDate)
}
next();
}
}]);

After

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { ApolloLink } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';

const httpLink = createHttpLink({ uri: '/graphql' });
const addDatesLink = new ApolloLink((operation, forward) => {
return forward(operation).map((response) => {
if (response.data.user.lastLoginDate) {
response.data.user.lastLoginDate = new Date(response.data.user.lastLoginDate)
}
return response;
})
});

// use with apollo-client
const link = addDatesLink.concat(httpLink);

Afterware (authentication via headers)

Before

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { ApolloClient, createNetworkInterface } from 'react-apollo';

const networkInterface = createNetworkInterface({
uri: '/graphql',
});
networkInterface.useAfter([
{
applyAfterware({ response: { headers } }, next) {
const token = headers.get('token');

if (token) {
localStorage.setItem('token', token);
}

next();
},
},
]);

const client = new ApolloClient({
networkInterface,
});

After

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import { ApolloLink } from 'apollo-link';
import { createHttpLink } from 'apollo-link-http';

const httpLink = createHttpLink({ uri: '/graphql' });
const afterwareLink = new ApolloLink((operation, forward) => {
return forward(operation).map(response => {
const context = operation.getContext();
const { response: { headers } } = context;

if (headers) {
const token = headers.get("token");

if (token) {
localStorage.setItem("token", token);
}

}
return response;
});
});

// use with apollo-client
const link = afterwareLink.concat(httpLink);

For more information on using Apollo Links, check out the link docs!;

Replacing Redux


The 2.0 moves away from using Redux as the caching layer in favor of Apollo maintaining its own store through the provided cache passed when creating a client. This allows the new version to be more flexible around how data is cached, and opens the storage of data to many new avenues and view integrations. If you were previously using the Redux integration to do something like logging, you can now use an Apollo Link to log whenever a request is sent to the server. For example:

1
2
3
4
5
6
7
8
9
import { ApolloLink } from 'apollo-link';

const logger = new ApolloLink((operation, forward) => {
console.log(operation.operationName);
return forward(operation).map((result) => {
console.log(`received result from ${operation.operationName}`);
return result;
})
});

Ultimately we think the move off Redux will open the door for more powerful cache implementations and further performance gains. If you were using the Redux integration for other uses, please reach out or open an issue so we can help find a solution with the 2.0!

If you were using Redux for other purposes in your app with an older version of Apollo, you may have been relying on the fact that the <ApolloProvider store=store> component did double duty as a react-redux <Provider>. In react-apollo 2.0, <ApolloProvider> no longer has <Provider> semantics; you’ll have to use <Provider> yourself.

Query Reducers


Query reducers have been finally removed in the 2.0, instead we recommend using the more flexible update API instead of reducer.

Edit on GitHub