How to use Apollo Client to Connect a React Frontend to a GraphQL API


In our previous article, Exploring JavaScript Client Libraries for GraphQL, we examined a variety of different GraphQL clients. In this article we will build a React frontend from scratch and focus on integrating Apollo Client.
The client will query a GraphQL API created from the JSONPlaceholder API. We are able to use the @rest
directive to easily translate the JSONPlaceholder API into a GraphQL schema.
You can find all of the code at the following repo.
1. Project Setup
We will not use a CLI tool like create-react-app
to setup our project. Instead we will create all of our files and directories from scratch.
Create root directory and initialize package.json
Our project will be called stepzen-react-tutorial
.
mkdir stepzen-react-tutorial
cd stepzen-react-tutorial
npm init -y
Add necessary dependencies
npm i @apollo/react-hooks apollo-boost graphql react react-dom react-scripts
Our dependencies will include the following React packages:
react
- Contains only the functionality necessary to define componentsreact-dom
- Entry point to the DOMreact-scripts
- Scripts and configuration used by CRA
And the following GraphQL and Apollo packages:
@apollo/react-hooks
- Common hooks for queries, mutations, and subscriptionsapollo-boost
- Pre-configured defaults for Apollo Clientgraphql
- JavaScript reference implementation for GraphQL
Add scripts to package.json
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
npm start
- Runs the app in the development mode onhttp://localhost:3000
npm test
- Launches the test runner in interactive watch modenpm run build
- Builds the app for production to thebuild
foldernpm run eject
- Removes the single build dependency from your project and exports configuration files and dependencies into your project as dependencies inpackage.json
.
Add browserslist to package.json
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
Your entire package.json
should look something like this:
{
"name": "stepzen-react-tutorial",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"keywords": [],
"author": "",
"description": "",
"dependencies": {
"@apollo/react-hooks": "^4.0.0",
"apollo-boost": "^0.4.9",
"graphql": "^15.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "^4.0.3"
}
}
2. Setup React App
To setup our React frontend create directories for src
, src/pages
, and public
.
mkdir src src/pages public
Create an index.js
file for our root component, an index.html
file to load the root component, and a HomePage
component.
touch src/index.js src/pages/HomePage.js public/index.html
Create a home page with a title
// src/pages/HomePage.js
export default function HomePage() {
return (
<>
<h1>StepZen React Tutorial</h1>
</>
)
}
Create root component to render the HomePage component
// src/index.js
import React from "react"
import { render } from "react-dom"
import HomePage from "./pages/HomePage"
render(
<React.StrictMode>
<HomePage />
</React.StrictMode>,
document.getElementById("root")
)
Create html boilerplate with root div
Our index.html
file includes a simple boilerplate with a root <div>
to load in our application.
<!-- public/index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1"
/>
<meta
name="theme-color"
content="#000000"
/>
<meta
name="description"
content="How to use Apollo Client to Connect a React Frontend to a GraphQL API"
/>
<title>React + StepZen App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
-->
</body>
</html>
Start the development server on localhost:3000
with the following command:
npm start
Create a Users component
The Users
component will fetch user data from the JSONPlaceholder API.
mkdir src/components
touch src/components/Users.js
We will need to create our backend before we can fetch any data. For now just include a placeholder title for Users.
// src/components/Users.js
export default function Users() {
return (
<>
<h2>Users</h2>
</>
)
}
Import the Users
component into HomePage.js
and return <Users />
.
// src/pages/HomePage.js
import Users from "../components/Users"
export default function HomePage() {
return (
<>
<h1>StepZen React Tutorial</h1>
<Users />
</>
)
}
Return to the browser to see the Users title displayed beneath the initial title.
3. Setup GraphQL API
To setup our StepZen API create a stepzen
directory containing a schema
directory.
mkdir stepzen stepzen/schema
Create an index.graphql
file for our schema
and a users.graphql
file for our User
type and Query
type.
touch stepzen/schema/users.graphql stepzen/index.graphql
index.graphql defines all files making up the GraphQL schema
Every StepZen project requires an index.graphql
that ties together all of our schemas. For this example we just have the users.graphql
file included in our @sdl
directive. The @sdl
directive is a StepZen directive that specifies the list of files to assemble.
# stepzen/index.graphql
schema
@sdl(
files: [ "schema/users.graphql" ]
) {
query: Query
}
The User
type includes an id
for each User
and information about the User
such as their name
and email
.
# stepzen/schema/users.graphql
type User {
id: ID!
name: String!
username: String!
email: String!
phone: String!
website: String!
}
type Query {
getUsers: [User]
@rest(
endpoint:"https://jsonplaceholder.typicode.com/users"
)
}
For our Query
we just have a single query called getUsers
that returns an array of User
objects. The @rest
directive accepts the endpoint
from JSONPlaceholder.
Deploy API
The stepzen start
command uploads and deploys your API automatically.
stepzen start --dashboard=local
The default way to test your GraphQL endpoint is from the StepZen dashboard explorer. You can get a local GraphiQL IDE by running
stepzen start
with the--dashboard=local
flag.
It will ask you to include a name with the format folder/name
. This is the destination you specify for your endpoint. I named my endpoint stepzen-react/users
.
A browser window with a GraphiQL query editor can be used to query your new endpoint on localhost:5001
. Enter the following query to test your endpoint:
query getUsers {
getUsers {
id
name
}
}
Set environment variables
This also deployed our API to https://username.stepzen.net/stepzen-react/users/__graphql
. We will use this endpoint for our environment variables. Create a .env
file along with a .gitignore
file.
touch .env .gitignore
Include .env
and node_modules
in your .gitignore
file to prevent them from being included if we push the project to a public GitHub repository.
node_modules
.env
In .env
set:
- Your StepZen API key to
REACT_APP_STEPZEN_API_KEY
- The endpoint with your username provided by
stepzen start
toREACT_APP_STEPZEN_ENDPOINT
REACT_APP_STEPZEN_API_KEY=<YOUR_API_KEY_HERE>
REACT_APP_STEPZEN_ENDPOINT=<YOUR_ENDPOINT_HERE>
4. Setup Apollo Client
There are many different ways to make GraphQL requests as described in Brian's previously mentioned article. We will use Apollo Client since it is familiar to many React developers, but future articles will explore alternative implementations.
Create utils
directory and client.js
file
mkdir src/utils
touch src/utils/client.js
Import ApolloClient
and instantiate a new client
instance. Set your API key with an authorization header. Set your endpoint to uri
.
// src/utils/client.js
import ApolloClient from "apollo-boost"
const {
REACT_APP_STEPZEN_API_KEY,
REACT_APP_STEPZEN_ENDPOINT
} = process.env
export const client = new ApolloClient({
headers: {
Authorization: `Apikey ${REACT_APP_STEPZEN_API_KEY}`,
},
uri: REACT_APP_STEPZEN_ENDPOINT,
})
Wrap HomePage
with ApolloProvider
Return to index.js
and import ApolloProvider
and client
from the utils
directory. Wrap the HomePage
component with ApolloProvider
and pass the client
prop with our environment variables to the provider.
// src/index.js
import React from "react"
import { render } from "react-dom"
import { ApolloProvider } from "@apollo/react-hooks"
import { client } from "./utils/client"
import HomePage from "./pages/HomePage"
render(
<React.StrictMode>
<ApolloProvider client={client}>
<HomePage />
</ApolloProvider>
</React.StrictMode>,
document.getElementById("root")
)
Create queries
directory and getUsers.js
file
mkdir src/queries
touch src/queries/getUsers.js
Import gql
and initialize GET_USERS_QUERY
with a query
called getUsers
. This query returns the id
and name
of the users.
// src/queries/getUsers.js
import { gql } from "graphql-tag"
export const GET_USERS_QUERY = gql`
query getUsers {
getUsers {
id
name
}
}
`
Pass GET_USERS_QUERY
into useQuery
Return to the Users
component and import the useQuery
hook and GET_USERS_QUERY
from getUsers.js
.
// src/components/Users.js
import { useQuery } from "@apollo/react-hooks"
import { GET_USERS_QUERY } from "../queries/getUsers.js"
To perform the query, pass GET_USERS_QUERY
into the useQuery
hook.
// src/components/Users.js
export default function Users() {
const {
data,
loading,
error
} = useQuery(GET_USERS_QUERY)
}
We have three possible results from our query.
- Check
loading
and display loading message iftrue
. - Check
error
and display error message iftrue
. - If
data
was received successfully set the response tousers
.
// src/components/Users.js
export default function Users() {
const {
data,
loading,
error
} = useQuery(GET_USERS_QUERY)
const users = data?.getUsers
if (loading) return <p>Almost there...</p>
if (error) return <p>{error.message}</p>
}
The JSON.stringify()
method converts a JavaScript object or value to a JSON string, optionally replacing values if a replacer function is specified. The value of null
is provided so all properties of the object are included in the resulting JSON string.
// src/components/Users.js
return (
<>
<h2>Users</h2>
<pre>
{JSON.stringify(users, null, " ")}
</pre>
</>
)
Here is our complete component.
// src/components/Users.js
import { useQuery } from "@apollo/react-hooks"
import { GET_USERS_QUERY } from "../queries/getUsers.js"
export default function Users() {
const {
data,
loading,
error
} = useQuery(GET_USERS_QUERY)
const users = data?.getUsers
if (loading) return <p>Almost there...</p>
if (error) return <p>{error.message}</p>
return (
<>
<h2>Users</h2>
<pre>
{JSON.stringify(users, null, " ")}
</pre>
</>
)
}
Map over users
array
To display a list of each user we can map over the users
array. This will return:
- An unordered list of each
user
with the user'sid
set to thekey
property - Each user's
name
set to a list item
return (
<>
<h2>Users</h2>
{users.map(user => (
<ul key={user.id}>
<li>
{user.name}
</li>
</ul>
))}
</>
)
Next Steps
We have now seen how to setup our React frontend with Apollo Client to query a GraphQL API deployed to StepZen. With this foundation we could continue building out the project with other libraries such as React Router for linking between pages, Next.js for server-side rendering, or Tailwind CSS for styling.
However, we could not deploy our application in its current state because our client has access to our API keys. We would need to deploy a secure API route with a function containing our keys. This can be easily accomplished with frameworks such as RedwoodJS as we will see in future articles. This is because it includes an API side auto-configured out of the box to deploy a GraphQL handler to AWS Lambda.
You can find more example repositories at StepZen Samples. If you're not already part of the private alpha, you can request an invitation here.