How to use REST API with Apollo Client and GraphQL on a React/Next.js app
Apollo Client is a state management library like Redux which allows you to write GraphQL queries on the client-side. If you are building an application with a GraphQL server, Apollo Client is one of the most suitable state management libraries as it has an amazing caching mechanism.
In this tutorial, I will fetch data from the OMDb Rest API to fetch movies by implementing it with Apollo Client to demonstrate how Rest APIs can be used in parallel with GraphQL.
Getting Started
Create a Next.js app
npx create-next-app <YOUR-APP-NAME>
In the case of React, use create-react-app.
Install Apollo Client and any peerDependencies
npm i @apollo/client apollo-link-rest graphql qs graphql-anywhere
Creating an HTTP Link
This is your already existing GraphQL Endpoint.
import { HTTPLink } from "apollo-link";
const httpLink = new HttpLink({
uri: 'https://server.myapp.com/graphql',
// other link options...
});
Creating a Rest Link
In our case, it’s an OMDB API to fetch movies.
import { RestLink } from ‘apollo-link-rest’;const restLink = new RestLink({
uri: “https://www.omdbapi.com/",
// other link options...
});
Setting up our Apollo Client
Now that we have configured our Rest and HTTP links, we can set them up in the Apollo Client.
import { ApolloClient, InMemoryCache, ApolloLink } from ‘@apollo/client’;const client = new ApolloClient({ link: ApolloLink.split(operation => operation.getContext().clientName === "rest",
// The string "rest" and "clientName" can be anything you want restLink, // Apollo will send to this if clientName is "rest"
httpLink // Otherwise will send to this
), cache: new InMemoryCache() // other options...
});
Writing our GraphQL Queries
Note that there are two OMDb endpoints. First for fetching data for a single movie and second for fetching a list of movies.
Note: You can generate your API key from https://www.omdbapi.com/apikey.aspx
We must know that Apollo Client normalizes cache data based upon the typenames
Therefore we must perform typename patching.
We can perform typename patching directly while writing our gql
queries. But for the fields that contain sub-fields, it is advisable to add typename directly in the Apollo Client which I will illustrate in a while.
Fetch a single movie result by its movie ID
import { gql } from '@apollo/client';
export const getMovieQuery = gql` query($imdbID: String!) { movie(imdbID: $imdbID) @rest(type: "Movie", path: "?i={args.imdbID}&apikey=<YOUR_API>") { Error imdbID imdbRating Title Year Runtime Genre Director Country Plot Poster } }`;
Here, Movie
is assigned as the typename. imdbID
is used as a variable argument so that its value can be passed through the useQuery
hook.
Fetch movie results by search parameters and page number
import { gql } from '@apollo/client';
export const getMoviesQuery = gql` query($search: String!, $page: Int!) { movies(search: $search, page: $page) @rest(type: "Movies", path: "?s={args.search}&apikey=<YOUR_API>&page={args.page}") { Error totalResults Search { Title Year imdbID Poster } } }`;
Here, Movies
is assigned as the typename. search
and page
are used as a variable argument so that their values can be passed through the useQuery
hook.
Note that the Search
field contains sub-fields, therefore we must attach a typename to this field as well. We can directly attach a typename to the Search
field in the query itself using @type(name: "Movie")
directive, but it is advisable to modify the Rest Link instead.
Modifying our Rest Link
// ... same as previousconst restLink = new RestLink({ uri: "https://www.omdbapi.com/", // Extra code to be added: typePatcher: { Movies: data => { if (data.Search != null) { data.Search =
data.Search.map(search => ({
__typename: "Movie", ...search
}));
} return data;
}
}
});
Note: Movies
is the typename of the data fetched by our query. Therefore keep in mind that this name should be identical to the one used in the gql
Fetching data in our UI using useQuery hook
import { useQuery } from ‘@apollo/client’;// ...React/NextJS functionconst {loading, error, data} = useQuery(getMoviesQuery, { variables: { search, page: 1 },
context: { clientName: 'rest' }});if(loading) return 'Loading...';if(error) return 'No results found';console.log(data.movies);
Similarly,
import { useQuery } from ‘@apollo/client’;// ...React/NextJS functionconst {loading, error, data} = useQuery(getMovieQuery, { variables: { imdbID: id },
context: { clientName: 'rest' }});if(loading) return 'Loading...';if(error) return 'No results found';console.log(data.movie);
Note that we can use our GraphQL endpoints just like the way we do normally, that is without any context
object.
That’s it. We are all done!
Conclusion
We know that GraphQL provides an amazing approach to our client-server applications but this does not mean that it prevents us from using the already existing REST APIs. REST and GraphQL shall always exist parallelly.
References
More content at plainenglish.io