If you're integrating your React frontend with a GraphQL API, you may want to check out the Apollo client! I found it to be pretty straightforward to hook up.
In this post, we'll create a React project from scratch using create-react-app
, add in the Apollo GraphQL client, and then use the SpaceX GraphQL API to display data in our application.
Please give this post a π, π¦, and π if you want more introduction to GraphQL posts!
Creating a new React App
I'm going to use the yarn
package manager to create a new React app called react-with-apollo
. You can, of course, use npm
if you'd like.
yarn create react-app react-with-apollo
We can cd
into that directory and run yarn start
to make sure our default React app is up-and-running on port 3000.
cd react-with-apollo
yarn start
If we navigate to http://localhost:3000, we'll get something like this:
Adding GraphQL the Apollo Client
To use GraphQL with the Apollo client, we need to install them both as project dependencies. Let's do that with yarn.
yarn add graphql @apollo/client
The next thing we need to do is configure out Apollo client. Normally this might require figuring out authentication with your GraphQL server but, since the SpaceX API is public, we don't need to worry about that. Instead, we just need to configure the client with our GraphQL API endpoint. We can additionally specify any caching we want to do for our queries. In this basic example, we'll just do some in-memory caching.
In this example, we'll configure our client in our index.js
file and wrap our App
in the Apollo provider. In practice you should probably put your Apollo provider as the lowest common ancestor for any components that will need to fetch data from the GraphQL API.
index.html
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import { ApolloClient, InMemoryCache, ApolloProvider } from '@apollo/client';
const client = new ApolloClient({
uri: 'https://api.spacex.land/graphql/',
cache: new InMemoryCache(),
});
ReactDOM.render(
<React.StrictMode>
<ApolloProvider client={client}>
<App />
</ApolloProvider>
</React.StrictMode>,
document.getElementById('root')
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
Querying the API
We're now all set up to query the API! In our App.js
file, let's create a query that gets past SpaceX missions, including the date, launch site, and rocket. Of course, since this is GraphQL, it's trivial to grab all that information in one query.
We will include one variable in our query, numLaunches
, just so we can see how to use variables.
App.js
import { gql } from '@apollo/client';
const PAST_LAUNCHES = gql`
query GetPastLaunces($numLaunches: Int!) {
launchesPast(limit: $numLaunches) {
mission_name
launch_date_local
launch_site {
site_name_long
}
rocket {
rocket_name
}
}
}
`;
function App() {
// TBD
}
Now comes the part where we integrate the Apollo client. It has an incredibly handy useQuery
hook that does a lot of heavy lifting for us. Basically, we pass the query we just defined, and any query options (in our case, just variables), and the hook returns a loading
boolean, possibly an error
object, and the returned data
.
import { gql, useQuery } from '@apollo/client';
const PAST_LAUNCHES = gql`
query GetPastLaunces($numLaunches: Int!) {
launchesPast(limit: $numLaunches) {
mission_name
launch_date_local
launch_site {
site_name_long
}
rocket {
rocket_name
}
}
}
`;
function App() {
const { loading, error, data } = useQuery(PAST_LAUNCHES, {
variables: {
numLaunches: 10,
},
});
}
Here we can see that we have provided our PAST_LAUNCHES
query along with our numLaunches
parameter, which we have set for 10
right now.
Soβlet's use the information the hook is returning to us! Since we're just learning now we'll have a very simple interaction. If loading
is true
, we'll show the user a "Loading..." message, if error
is truthy, we'll tell the user something has gone wrong, and otherwise we'll format our query data in a readable way.
import { gql, useQuery } from '@apollo/client';
const PAST_LAUNCHES = gql`
query GetPastLaunces($numLaunches: Int!) {
launchesPast(limit: $numLaunches) {
mission_name
launch_date_local
launch_site {
site_name_long
}
rocket {
rocket_name
}
}
}
`;
function App() {
const { loading, error, data } = useQuery(PAST_LAUNCHES, {
variables: {
numLaunches: 10,
},
});
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Oh no!</p>;
}
return (
<ul>
{data.launchesPast.map((launch) => (
<li key={launch.mission_name}>
<strong>{launch.mission_name}</strong>
<ul>
<li>
Launch Date:{' '}
{new Date(launch.launch_date_local).toLocaleDateString()}
</li>
<li>Rocket: {launch.rocket.rocket_name}</li>
<li>Launch Site: {launch.launch_site.site_name_long}</li>
</ul>
</li>
))}
</ul>
);
}
export default App;
Of course, the structure of our data
is exactly the same as the structure of our input query; one of the more helpful GraphQL features!
Let's check out our web app and see if things look alright.
Perfect! Looks great to me.
Updating Variables
In the last part of this post, let's explore how easy it is to re-fetch data if we update our variables. In this case, we may want a different number of past launches.
Our hope might be that we can just maintain a separate numLaunches
stateful variable and, when we update that, we can cause te useQuery
hook to fire again. In the following example, we just add a button to show five launches instead of 10. Trivial, but you get the idea!
import { gql, useQuery } from '@apollo/client';
import { useState } from 'react';
const PAST_LAUNCHES = gql`
query GetPastLaunces($numLaunches: Int!) {
launchesPast(limit: $numLaunches) {
mission_name
launch_date_local
launch_site {
site_name_long
}
rocket {
rocket_name
}
}
}
`;
function App() {
const [numLaunches, setNumLaunches] = useState(10);
const { loading, error, data } = useQuery(PAST_LAUNCHES, {
variables: {
numLaunches,
},
});
if (loading) {
return <p>Loading...</p>;
}
if (error) {
return <p>Oh no!</p>;
}
return (
<>
<button onClick={() => setNumLaunches(5)}>Show 5</button>
<ul>
{data.launchesPast.map((launch) => (
<li key={launch.mission_name}>
<strong>{launch.mission_name}</strong>
<ul>
<li>
Launch Date:{' '}
{new Date(launch.launch_date_local).toLocaleDateString()}
</li>
<li>Rocket: {launch.rocket.rocket_name}</li>
<li>Launch Site: {launch.launch_site.site_name_long}</li>
</ul>
</li>
))}
</ul>
</>
);
}
export default App;
So does this work? Let's test it out.
You bet it does!
Concluding Thoughts
I'm enjoying the Apollo client with React quite a bit! It "just works" and offers the reactivity I need when executing GraphQL queries. Hopefully this post has helped you get started with GraphQL in React as well!
Top comments (6)
If you get "Failed to load config "react-app" to extend from." you will need to
yarn add eslint-config-react-app
Your reply saved me from a heart attack. Thanks :D
thx bro this help me a lot
Great example! I just finished the tutorial Apollo has in its site which uses the same API, for anyone else got engaged.
Not that it affects the code, but in
const PAST_LAUNCHES = gql`
query GetPastLaunces($numLaunches: Int!) {
'Launces' is probably intended to be Launches
It's so 'just works' that seems magic! Thanks!