Follow me on Twitter, happy to take your suggestions on topics or improvements /Chris
This article is part of series on Graphql
- Building a GraphQL server using Node.js and Express
- Building a GraphQL server using Node.js and the Apollo Framework
- Consuming an Apollo GraphQL server using React, we are here
This is an introduction to how we can interact with an Apollo GraphQL server from the frontend. If you are not familiar with how to create and Apollo Server, have a look at this article first Creating an Apollo Server
In this article we will go through:
- set up, We need to specify the URL to our server and instantiate a client
- query, There are Query components we can use to query for data
- mutation, We can execute mutations using the Mutation component
- polling/explicit fetch, Apollo comes with some nice functionality on polling, explicitly and fetching data
Set up
To set up a React app with GraphQL we need the libraries apollo-boost
and react-apollo
. apollo-boost
provides us with ApolloClient
that we need to instantiate given a URL
. react-apollo
gives us a Higher Order Provider ApolloProvider
that we need to wrap our application with. First off do the necessary installs:
yarn add react-apollo apollo-boost graphql
Once we’ve installed everything we are ready to set everything up. Head over to index.js
and type the following:
import React, { Component } from "react";
import ApolloClient from "apollo-boost";
import { ApolloProvider } from "react-apollo";
import Local from “./components/Local”;
const localGraphQL = "http://localhost:4000/graphql";
const client = new ApolloClient({
uri: localGraphQL
});
class App extends Component {
render() {
return (
<ApolloProvider client={client}>
<div>
<h2>My first Apollo app </h2>
</div>
</ApolloProvider>
);
}
}
export default App;
Above we are first instantiating ApolloClient
and in the process providing it with a url
, the location of our GraphQL server.
Secondly, we are wrapping the entire application with our ApolloProvider
and we also set its client
property with our ApolloClient
instance.
Now we are ready to interact with our GraphQL server.
Query
To be able to query an Apollo server we need to do three things:
- Write our
gql
query - Utilize the Query component the
react-apollo
library gives us - Render the response
To write our gql
query we need to import graphql-tag
and then write the GraphQL query, like so:
const getRates = gql`
{
rates(currency: “USD”) {
currency
rate
}
}`;
Thereafter we need to import the Query
component from react-apollo
and as input property provide the query we just defined, like so:
const Data = () => (
<Query query={getRates} >
// render the query results
</Query>
);
In the first child of our Query
component we are invoking a function that has an object as a parameter. The object has the following properties:
- loading, as long as our query hasn’t resolved this is
true
- error, if we get an error back from our query
- data, the data result from our query
Now that we understand the properties and how we can use them, let’s put everything together:
import React from "react";
import { Query } from "react-apollo";
import gql from "graphql-tag";
const getRates = gql`
{
products(type: "DVD") {
name
price
}
}`;
const Data = () => (
<Query query={getRates} >
{({ loading, error, data }) => {
if (loading) return <p>Loading…</p>;
if (error) return <p>Error :(</p>;
return data.products.map(({ name, price }) => (
<div key={name}>
<p>{`${name}: ${price}`}</p>
</div>
));
}}
</Query>
);
export default Data;
We have now learned how we can read data from a GraphQL server and present into to our user.
Polling
Not only do you want to fetch data but sometimes you also want to fetch data at a regular interval without explicitly navigating to a certain page or pressing a specific button for the GET request to be fired off. We use this in for example chat applications to achieve a sense of real-time. We are of course talking about polling, fetching data at a regular interval that we specify. The Query
component that we learned to use has polling built in and all we need to do is to set a pollInterval
property to the number of milliseconds we want between fetches. Let’s have a look at what that can look like:
import React from "react";
import { Query } from "react-apollo";
import gql from "graphql-tag";
const GET_DATA = gql`
{
products {
name
id
}
}
`;
const DataPull = () => (
<Query query={GET_DATA} pollInterval={500}>
{(loading, error, data, startPolling, stopPolling) => {
if (loading) return null;
if (error) return `Error!: ${error}`;
return (
<React.Fragment>
{data.products.map(p => <div>{p.name}</div>)}
<button onClick={()=> startPolling()}>Start polling</button>
<button onClick={() => stopPolling()}>Stop polling</button>
</React.Fragment>;
)
}}
</Query>
);
export default DataPull;
Above we have now introduced the following new concepts:
- pollInterval, this is something that expects polling interval in milliseconds, as you can see we set that to
500
, e.g half a second - startPolling, this is a function in which we can start the polling anew if we have previously stopped it
- stopPolling, this is a function that allows us to stop the polling any time we want
Refetch
Sometimes we end up with scenarios where we want to explicitly fetch the data to ensure we are looking at the latest. The reason for doing so is to react to a user action rather than polling. Let’s look at how we can use this refetch
functionality:
import React from "react";
import { Query } from "react-apollo";
import gql from "graphql-tag";
const GET_DATA = gql`
{
products {
name
id
}
}
`;
const Refetch = () => (
<Query query={GET_DATA}>
{(loading, error, data, refetch) => {
if (loading) return null;
if (error) return `Error!: ${error}`;
return (
<React.Fragment>
<div>
{data.prop}
<button onClick={() => refetch()}>Fetch</button>
</div>
</React.Fragment>
)
}}
</Query>
);
export default Refetch;
Above we see that we have added another argument refetch
to our Query
child function like so:
{(loading, error, data, refetch) => {
}}
This refetch
argument is a function that we can invoke so we can, therefore, wire it up to a button in our markup like so:
<button onClick={() => refetch()}>Fetch</button>
Mutation
When we do a mutation against a GraphQL server we need to do the following:
invoke the correct mutation
use the Mutation component from
react-apollo
The above doesn’t sound like much and it isn’t. So let’s start with the first thing, our mutation query:
We will be using the gql
helper from the graphql-tag
library to create our mutation query. Thereafter we use the keyword mutation
, followed by giving the mutation a name and specify its input parameter $person
. At this point we have the following query:
const ADD_PERSON = gql`
mutation AddPerson($person: Person!) {
}
`;
Now we are ready to call the actual mutation addPerson
that we defined in our GraphQL server. Your mutation query should now look like this:
const ADD_PERSON = gql`
mutation AddPerson($person: Person!) {
addPerson(person: $person) {
id
}
}
`;
Next up is putting the mutation query to use by working with our React component Mutation
. The component will need two things:
- populate the
mutation
property, - define the child of the
Mutation
component, we will need to provide this with a function that as its first argument contain themutation
function that will trigger the mutation to happen and as the second argument it will take an object with the propertiesdata
,error
andloading
Let’s start with the first bit of using the Mutation
component and set its mutation
property, like so:
import React from "react";
import { Mutation } from "react-apollo";
import gql from "graphql-tag";
const ADD_PERSON = gql`
mutation AddPerson($person: Person!) {
addPerson(person: $person) {
id
}
}
`;
<Mutation mutation={ADD_PERSON}>
</Mutation>
Above we have taken our Mutation
component in use and set the mutation
property with our mutation query ADD_PERSON
. Next up is defining the child of the Mutation
component. As we already stated that child is a function like so:
(addPerson, { data, loading, error }) => (
// JSX
)
The function above is expected to return JSX. We are expected to define a piece of JSX that lets us use the following:
- addPerson(), this function that will carry out the mutation query.
- loading, this boolean will tell us whether our mutation is ongoing or not, use this value to determine whether to use a spinner or not
- data, this is the data that comes back after your mutation query finished
Now that we understand what the function parameters are for, let’s define our JSX. It is quite customary to define a Form when we want to collect data, so let’s do that:
<form onSubmit={e => {
e.preventDefault();
addPerson({ variables: { person: { name: input.value } } });
input.value = “”;
}} >
<input ref={node => { input = node; }} />
<button type=”submit”>Add Person</button>
{loading &&
<div>adding person…</div>
}
{ data &&
<div>response data</div>
}
{ error &&
<div>Error adding person…</div>
}
</form>
As you can see above we have our Form and one input field and a button that we can press. We hook up the addPerson()
method to the onSubmit()
of the form. Note that we also solve how we get data to our mutation query. We give the addPerson()
method an object that has a property variables
in which we assign an object to the property person
. That person
property is the same input parameter that exist on our mutation query.
The other fields data
, loading
and error
are used as conditional JSX where we choose to show them if they are truthy.
That’s it, that is all there is to it to invoke a mutation with some parameters and show the response, whether actual data or an error.
Below is the entire code in its entirety.
import React from "react";
import { Mutation } from "react-apollo";
import gql from "graphql-tag";
const ADD_PERSON = gql`
mutation AddPerson($person: Person!) {
addPerson(person: $person) {
id
}
}
`;
const DataInput = () => {
let input;
return (
<Mutation mutation={ADD_PERSON}>
{(addPerson, { data, loading, error }) => (
<div>
<form onSubmit={e => {
e.preventDefault();
addPerson({ variables: { person: { name: input.value } } });
input.value = “”;
}} >
<input ref={node => { input = node; }} />
<button type=”submit”>Add Person</button>
{loading &&
<div>adding person…</div>
}
{ data &&
<div>response data</div>
}
{ error &&
<div>Error adding person…</div>
}
</form>
</div>
)}
</Mutation>)
}
export default DataInput;
Summary
We have looked at different ways to interact with data from the backend.
Fetching data, if we use the
Query
component we can fetch data by populating itsquery
attribute with agql
questionPolling for data, if we set the attribute
pollInterval
on theQuery
component we can poll against our GraphQL backend.Explicit data fetch, by using the extra argument
refetch
, we could explicitly fetch data when we wanted itTriggering mutations, we learned that we could use the
Mutation
component to carry out mutations
Top comments (0)