Part 1: Skip the server, REST calls from Apollo Client!
apollo-link-rest allows you to call REST APIs from a GraphQL client with little effort. It’s straight forward, and allows you to get up and running very quickly.
Why would you want to do this? This is a great way for prototyping and working with GraphQL without being dependant on having a GraphQL API to query! You’ll be able to integrate with any 3rd party service that provides a REST API.
There’s more benefits as well. Other than no server-side code required, there’s very little infrastructure needed to get started. You can mix API sources and have a unified infrastructure for all of your endpoints. You can mix in an actual GraphQL endpoint, internal and external. If you had your own services for example, you could have that data come in through the same GraphQL queries alongside incoming data from apollo-link-rest.
Apollo Client out of the box Data is managed by Apollo Client. Apollo Client will cache and normalize your results out of the box when using apollo-cache-inmemory. It will always return a __typename and id and keep everything synchronized throughout your app. This will enable you to not have to worry about massaging data into a shape that is consistent, normalized and accessible across your app’s code base. Once a rest endpoint is set up, you won’t have to think differently between different data sources, and it removes context switching between multiple api endpoints. More benefits of Apollo Client include Optimistic UI, Error handling out of the box, Pagination, Server-side rendering, and generally improved performance. Apollo Client will make it a lot easier to transition to GraphQL fully when you want to.
Building a simple sneaker recommender app We’ll build a simple client side web app using the super powers gifted to us by apollo-link-rest. It will allow us to recommend pairs of sneakers as well as finding us our own pair of red sneakers.
Let’s get started by checking out the first REST API, snkrs.co/recommendations. It can take a bunch of different parameters like the color and type of shoe laces.
NOTE: This is not a real REST API, but let’s pretend it’s the real thing for now. :)
When we curl snkrs.co/recommendations?color=red let’s see what we get back as a response.
{ "hits": [ "5acfb98f93a954721d9982a3", "5989d75147d42c179e4821a9", "5ad9f3b90038dc61d13b630e", "5980b81e0952e830dce61904", "5980b81e0952e830dce616c8", "5980b81e0952e830dce61ab4" ] } We get a response back with what looks like id‘s of some sneakers that are red!
If this were a GraphQL endpoint, the query might look something like this to retrieve the same response.
query recommendations($favoriteColor: String) { sneakers(color: $favoriteColor) { hits { id } } } Next let’s start using this sneakers REST API to find some cool red sneakers.
import React from 'react';
import { AppolloProvider } from 'react-apollo';
import App from './App';
import apolloClient from './apolloClient';
export default class extends React.Component {
render() {
return (
<ApolloProvider client={apolloClient}>
<App />
</ApolloProvider>
);
}
}
First, we will wrap our app with an ApolloProvider which comes from react-apollo. ApolloProvider takes a client prop, which we’ll create in the following step. For now that’s all the setup we need to do for our app to start consuming GraphQL.
Now we need to build a client for <ApolloProvider />
.
import { ApolloClient } from 'apollo-client';
import { RestLink } from 'apollo-link-rest';
import { InMemoryCache } from 'apollo-cache-inmemory';
const link = new RestLink({
endpoints: {
sneakerRecommender: 'https://snkrs.co/recommendations',
},
});
const apolloClient = new ApolloClient({
link,
cache: new InMemoryCache(),
});
export default apolloClient;
We are going to construct a new RestLink and add sneakerRecommender as one of our endpoints. For the cache, we will use apollo-cache-inmemory, which will store data right on the connected client.
To use our new endpoint we will need to add a @rest directive to our GraphQL query. The @rest directive takes a type, endpoint, and path.
The type is something we will use to generate a __typename for the output we get back from our endpoint. We’ll discuss this point later on in more detail. The endpoint maps to what we called it in our Apollo Client, sneakerRecommender. The path is what gets appended in the REST API URL. So for us, we are querying sneakers by color.
query recommendations($favoriteColor: String) {
sneakers(colors: $favoriteColor)
@rest(
type: "RecommendationResult"
endpoint: "sneakerRecommender"
path: "?color=:favoriteColor"
) {
hits {
id
}
}
}
Here’s our initial query. It takes a $favoriteColor, and returns an array of id‘s of sneakers that are red. The @rest directive defines how we will pass the variable $favoriteColor to the REST API.
You can use rest directives at any depth in a query. Once you define a rest directive, everything nested within the directive must match to the original shape our data was in when we received it from the REST API. In our example, we will have an array called hits and id‘s within that.
What about typename? We want our sneaker recommendations to be cached. If we switch the color to blue we don’t want to lose the results from our previous query, that’ll give us a nice buttery smooth experience when we decide to query for the original red sneakers, since those results will already be cached.
In Apollo Client, most data is stored as a flat array. It won’t know if your id‘s are unique, so it will use a __typename + id to create an internal symbol. When you build bigger and more complex GraphQL schemas, it is strongly typed. You will typically include a __typename, so this internal symbol is generated from those fields and you won’t have to worry about this. Because our sneaker API doesn’t return us objects with a __typename field, we will patch it in ourselves.
import { ApolloClient } from 'apollo-client';
import { RestLink } from 'apollo-link-rest';
import { InMemoryCache } from 'apollo-cache-inmemory';
const link = new RestLink({
endpoints: {
sneakerRecommender: 'https://snkrs.co/recommendations',
},
typePatcher: {
RecommendaitonResult: data => {
data.hits = data.hits.map(id => ({
__typename: 'Recommendation',
id,
}));
return data;
},
},
});
const apolloClient = newApolloClient({
link,
cache: new InMemoryCache(),
});
export default apolloClient;
Let’s add a typePatcher to our Apollo Client. This gives us is the ability to add a __typename to pieces of data we care about that may be deeply nested within a response. For us, the data we care about is nested within hits. We will map over hits, and return objects instead of just strings in the hits array. Our new sneaker object will have an id, and __typename!
Let’s take a look at what data looks like returned from this recommendations typePatcher.
"hits": [
{
"__typename": "Recommendation",
"id": "5acfb98f93a954721d9982a3",
},
{
"__typename": "Recommendation",
"id": "5989d75147d42c179e4821a9",
},
...
]
That looks a lot better.
You won’t always need to create a typePatcher I chose this example to show off how to use it. If the raw data comes back as an array of objects with an id on each result, Apollo Client will do the work for you and append a __typename on each result automatically.
Showing off our shiny red shoes Now that Apollo Client will know to cache all these results in memory on the client, let’s see how this all comes together in a React component.
import React from 'react';
import { Query } from 'react-apollo';
import RECOMMENDATION_QUERY from './recommendationQuery.js';
import Sneakers from '../components/Sneakers';
import Loading from '../components/Loading';
class Recommendations extends React.Component {
render() {
const { favoriteColor } = this.props;
return (
<Query query={RECOMMENDATION_QUERY} variables={{ favoriteColor }}>
{({ loading, data })} => {
if (loading) return <Loading />;
return <Sneakers recommendations={data.results} />;
}}
<Query>
);
}
}
export default Recommendations;
We are going import the query file we created, and create a new <Query>
component that takes our recommendation query and some variables.
favoriteColor comes in from props, loading is a boolean that is provided by the query component, and our sneaker data comes in via data. We don’t need to worry about catching any empty states within our sneakers component. Once loading is false, and data full of our sneaker results, we will return a Sneakers component and we will pass in our recommendations to it.
Review You can easily start to consume 3rd party REST API’s very fast with little effort. apollo-rest-link is great, give it a try!
I gave a similar talk on apollo-rest-link, you can watch it here:
In the second part of this post, will dive into nested @rest directives, and using multiple query components in your layout for more granular control.
When it becomes available, I’ll link it here.
In the mean time, check out some of our other GraphQL blog posts written by others on the team.