What is GraphQL?
Unlike most traditional REST APIs, GraphQL APIs only have a single endpoint to retrieve all the required data for your app.
“GraphQL API Support Added to RapidAPI Marketplace”, Kelsey, January 21, 2020
If your first question when reading this article is “What is GraqhQL” then I would recommend checking out this article, which will get you caught up to speed. Or, you can jump to a specific section in the article with these links:
What’s the difference between GraphQL and RapidQL?
RapidQL and GraphQL are both open source. Furthermore, they try to serve a similar purpose. They are both trying to simplify API calls by aggregating and shrinking responses by only extracting relevant data. They leverage the opportunity for specificity in an HTTP request.
RapidQL is a project that can be installed using NPM and has a somewhat different syntax than GraphQL.
Take this code snippet for example from RapidQL’s documentation;
const RapidQL = require('RapidQL'); let rql = new RapidQL({ }); rql.query(`{ Http.get( url:"https://joke3.p.rapidapi.com/v1/joke" ){ content, Http.post( url: "https://api.twilio.com/.../Messages.json", form: { Body: content, From: from, To: to } ){ to, status, uri } } }`);
In the code above, two APIs are used and the response from the first is passed as an argument to the second. This is useful for reducing the size of data down to just what you need to pass on to the next API.
Next, take a look at this GraqhQL query;
query business($limit: Int, $id: String) { business(id: $id) { name id url phone categories { title alias } } }
This query would typically be a string passed to an API that has support for GrapqhQL queries. It’s worth noting that an HTTP library (like Axios) would need to be used to send this query to the API.
Regardless, two queries have a fundamentally different syntax. Also, RapidQL has been specifically designed to serve the RapidAPI marketplace.
However, this link goes to a list of APIs that have support for GraphQL on RapidAPI, but you can use RapidQL for any API on the marketplace. That is the key difference. There is a handful of APIs that support GraphQL, but RapidQL can be used with any API.
Today, we will be discussing how to fetch data using React from one of the APIs that support GraphQL. What’s useful with this example is the GraphQL queries—and syntax—will be the same for any API that supports GraqhQL.
Reddit GraphQL API Proxy
Reddit is an American social news aggregation, web content rating, and discussion website.
The Reddit GraphQL API is a perfect example to showcase how GraphQL can be so special. If you visit the API’s dashboard with this link and select the Queries dropdown on the left, you can glean some of the data that can be extracted;
Twitter, Github, and Reddit make for a useful lineup! If you have used RapidAPI before you’ll notice that this dashboard is slightly different than a normal API dashboard on RapidAPI.
However, we can make a few quick assumptions.
- Queries ask for data
- Mutations change data
Fragments are smaller pieces of queries that we discuss later.
We won’t be covering mutations in the example, but queries and fragments will soon make more sense.
With queries and fragments, we can access an assortment of public data on Reddit without having to use an account. We could pull metadata from a user’s Twitter account or comments from a Subreddit.
And this can all be done with one endpoint and one query! I hope that you are excited to build something with this API because it’s time to start!
How to use a GraphQL API with React
Project Overview
The project is built using the Next.js Javascript framework. Next.js is a React framework for server-side rendered apps, static websites, and ZEIT is a platform that is specifically designed for hosting Next.js applications.
I like to use Next.js because the framework, combined with ZEIT, offers a relatively easy way to set up API routes—without a backend server—that can use secret environment variables. Keeping an API-key safe is important when accessing third-party APIs (like using an API marketplace).
The project has a few display components, one API route, and one page. By the end, this is what we will have!
I will try to focus on the GraphQL, and React specific aspects of the project, but if you are curious about learning more with Next.js they have a nice tutorial on their site.
Prerequisites
- Node.js installed on your system
- Text editor for writing the code
- Understand how to use the terminal (i.e Bash, Powershell) to execute commands
- Internet connection
- Next.js account on ZEIT so we can test and develop locally using secret variables
- Understanding of Javascript and ReactJS
1. Set-up Project
Open up a new terminal, or use your text editor, to create a new project folder named rapidapi-graphql-react
. Change directories into the new folder.
In the new directory, initialize a new NPM project and install some libraries with;
$ npm init -y $ npm install --save react react-dom next axios
If you have not already opened up a text editor in the project root, do that now, but keep the terminal open.
Create a directory named pages
. The spelling is important with Next.js. Inside of pages
create index.js
, and also inside of the pages
folder create a directory named api
with a file called graphql.js
.
Add the below code to index.js
.
import React from 'react' export default function Index() { return ( <div> <h1>Using a GraqhQL Api with React.js</h1> </div> ); }
Next, create an empty directory with the name components
at the root project level.
Finally, create three more files (that are used for our environment variables) at the project root.
now.json
next.config.js
.env.build
The project directory should resemble the image below (I am using Visual Studio Code so your file structure may look a little different).
Environment Variables
In now.json
add the code,
{ "build": { "env": { "RAPIDAPI_KEY": "@rapidapi_key" } } }
Later, we’ll use the Now-CLI to develop locally. This file is telling the project to load in an environment variable from named rapidapi_key
(expects lowercase, even though the declarations are uppercase in other files).
The Now-CLI is in beta, so some steps we are taking might not be the right steps if you are doing this in the future.
In next.config.js
, load in the environment variable from .env.build
;
module.exports = { env: { RAPIDAPI_KEY: process.env.RAPIDAPI_KEY, }, }
Finally, declare the RAPIDAPI_KEY
variable in .env.build
. If you know where to find your key, you can add the value now, if not, I will point it out later.
# .env.build # Development keys, for production we recommend Now Secrets RAPIDAPI_KEY=yourapikey
Now-CLI
Later, we use the Now-CLI to run a local development server. Therefore, we need to download the CLI and prepare it for development work.
Download the Now-CLI by running npm i -g now
in the project root.
Login to Now with:
now login
Follow the prompts. Do not link to an existing project and do not override the settings.
An email will be sent to your ZEIT account’s email and you can verify. The site has to be deployed before we go any further. This is strange, but you can shut down the project later on ZEIT after we are done.
In the next section, we can start adding more code!
2. Sign Up For a Free Account on RapidAPI
To use the Reddit GraqhQL API in the next section, you will need an account. Visit RapidAPI to get signed up!
3. Fetch GraphQL Data
Add a button, function call, and state variable to the component in index.js
.
import React from 'react' import axios from 'axios' export default function Index() { let [data, setData] = React.useState('') const getData = () => { axios({ method: 'GET', url: '/api/graphql', }) .then(response => { // add data to state setData(response.data.data) }) .catch(error => { setLoading(false) console.log(error) }) } return ( <div> <h1>Using a GraqhQL Api with React.js</h1> <button type='button' onClick={getData}>Get Data</button> <pre> <code> {JSON.stringify(data, null, 4)} </code> </pre> </div> ); }
This file now has a button that fetches data from the API routes and displays the JSON response on the page.
Next, let’s create the API route.
Add the below code to graphql.js
,
import axios from 'axios'; export default async (req, res) => { // Destructure incoming request const { method, } = req if (method != 'GET') { return res.status(405).send() } try { // gets attractions based on lat long variables const response = await axios({ "method": "POST", "url": "https://reddit-graphql-proxy.p.rapidapi.com/graphql", "headers": { "content-type": "application/json", "x-rapidapi-host": "reddit-graphql-proxy.p.rapidapi.com", "x-rapidapi-key": process.env.RAPIDAPI_KEY, "accept": "application/json" }, "data": { "query": `query BigQuery($name: String!){ reddit { subreddit(name: $name){ newListings(limit: 4) { title url } } } } `, "operationName": "BigQuery", "variables": { "name": "Coronavirus", } } }) res.send(response.data) } catch (e) { // returns bad request error if something goes wrong res.status(400).send() } }
This setup will look familiar if you have used the express library for setting up server API routes. Request and response objects are passed in the route (/api/graphql
) and can carry data, query parameters, headers, etc. Then, the response object is returned with data or an error.
If the function receives a GET request, it will call the Reddit GraqhQL API, using the Axios library and return the data to the front end.
Before testing out the function we need to explore the RapidAPI dashboard. Visit the Reddit GraqhQL Proxy API page.
RapidAPI Dashboard
The interactive dashboard has three main sections.
- On the left, there are the query options (or endpoints) for the API
- In the middle, there is an interactive query parameter section
- On the right, there’s code snippets and sample responses
Hover your mouse over reddit on the left section of the dashboard and click the yellowish Add Query button that appears.
Look at the bottom of the middle section on the dashboard and notice that the Query and Variables sections have been filled with code.
This is a GraqhQL query! GraqhQL queries can have;
- an operation type (query, mutation, fragment)
- operation name
- variables
- query parameters
- variable values
The query that appeared is very large, and the code snippet generation tool isn’t very helpful in this case. Instead, copy the query value from graqhql.js
into the middle section’s GQL Query box. Here’s the query to copy and paste;
query BigQuery($name: String!){ reddit { subreddit(name: $name){ newListings(limit: 4) { title url } } } }
Let’s break this down, the operation;
- type is a query
- name is
BigQuery
- has a variable,
$name
, of typeString!
- queries for
title
andurl
parameters
We are querying the newListings
of a given subreddit
, and limiting the response to four (limit: 4
) inside the data category reddit
.
Notice that the $name
variable is passed as an argument to subreddit
. Next, let’s declare the $name
variable in the dashboard.
Add the name
parameter to the Variables input. You can give it any subreddit name.
Test the query by hitting the blue Test Endpoint button.
Look at the bottom right of the dashboard to see the response object.
Well done! Let’s expand the query.
Fragments
Let’s say we want to query two subreddits at once. Could we simply add another variable (call it name2
) and duplicate the query? Not quite. We have to give the queries unique names. If we didn’t, there would be an error because we would have duplicate parameters on the response object.
Instead, add names to the beginning of the queries in the dashboard. Call them firstSubreddit
and secondSubreddit
Don’t forget to add the variable to the box below. I chose “politics” as the value for name2
.
Notice that this query is not very DRY. What if we were to query ten items off of the subreddit instead of 2? It would be messy. Let’s add a fragment to make things easier.
Next, let’s break down the fragment:
- the operation type is
fragment
- the operation name is
comparisonFields
- the parameters are below (I added a few)
Notice that this operation is performed on RedditLink
. This is a type of object that is defined by the Reddit GraqhQL schema. The types can be found using the left section of the RapidAPI dashboard.
Query Types
The left section of the dashboard can be very useful. By following the path down into a query one can discover arguments, types, and fields that can be passed to a specific query.
You will probably use fragments frequently, and knowing how to easily find types can save you time in the future.
Expanding the Query
That is the only fragment that we use in the app. However, let’s see if we can stretch this query for more data.
Add the following queries for twitter
and giphy
at the same depth as the current reddit
query.
twitter { search(q: $q, count: $count) { id created_at text retweet_count user { screen_name name profile_image_url followers_count } } } giphy { search(query: "#coronavirus", rating: r){ id url images { fixed_width_downsampled { url } } } }
The full query (in the dashboard) is now:
query BigQuery($name: String!, $name2: String!, $q: String!, $count: Int){ reddit { firstSubreddit: subreddit(name: $name){ newListings(limit: 4) { ...comparisonFields } } secondSubreddit: subreddit(name: $name2){ newListings(limit: 4) { ...comparisonFields } } } twitter { search(q: $q, count: $count) { id created_at text retweet_count user { screen_name name profile_image_url followers_count } } } giphy { search(query: "#coronavirus", rating: r){ id url images { fixed_width_downsampled { url } } } } } fragment comparisonFields on RedditLink { title url comments { body } }
I snuck two more variables into the query parameters that need to be added to the Variables input before testing the query.
Testing the query should now return the full data object that the application needs.
Test GraqhQL Query
Don’t add the big query to the application yet. Take a look at the current API call in graphql.js
, specifically the data
parameter.
"data": { "query": ` query BigQuery($name: String!){ reddit { subreddit(name: $name){ newListings(limit: 4) { title url } } } } `, "operationName": "BigQuery", "variables": { "name": "Coronavirus", } }
The set up is similar to what we had been doing in the dashboard with the added exception that we have to include an operationName
argument that matches the name of our main query.
Don’t forget to add your RapidAPI key to .env.build
. You can find it on the dashboard in the middle or far-right sections. Mine is blocked out in this image.
Before starting the development server open up package.json
and replace the scripts block with the following Next.js commands:
... "scripts": { "dev": "next", "build": "next build", "start": "next start" } ...
In the terminal, start the development server by running now dev
.
Visit http://localhost:3000 and hit the Get Data button. The response object should appear on the page.
Add the dashboard query and variables
Replace the query inside of graphql.js
with the query and fragment from the dashboard.
Next, add the variables from the dashboard to the variables
parameter object. There should be a giant response object printed to the screen after you hit the Get Data button.
4. Display the GraphQL Data Response
Next, add the following three components to the components
folder in separate files.
Giphy.js
import React from 'react' const Giphy = (props) => ( <> <a target="blank" href={props.giphyUrl} rel="noopener noreferrer" className="gif"> <img key={props.id} src={props.url} alt="giphy image" /> </a> <style jsx>{` .gif { display: inline; margin: 3px; padding: 3px } a { text-decoration: none; } `} </style> </> ) export default Giphy
Subreddit.js
import React from 'react' const Comment = (props) => ( <> <p>{props.text}</p> <style jsx>{` p { max-width: 60%; margin: .5rem auto; padding: .3rem; font-style: italic; text-align: left; } `} </style> </> ) const Subreddit = (props) => ( <div className="wrapper"> <a target="_blank" rel="noopener noreferrer" href={props.url}>{props.title}</a> {props.comments.map((comment, i) => <Comment key={i} text={comment.body} />)} <style jsx>{` div { text-align: center; } a { text-decoration: none; font-size: 2rem; font-weight: 100; margin: auto; } .wrapper { margin-top: .75rem; margin-bottom: .75rem; padding: 1rem; } `} </style> </div> ) export default Subreddit
Twitter.js
import React from 'react' const Twitter = (props) => ( <div className="main-wrapper"> <div className="user-wrapper"> <> <img src={props.profileImageUrl} alt="" /> </> <div className="user-info"> <span><b>{props.name}</b></span> <span>{props.screen_name}</span> <small>{props.followers} followers</small> <span>{new Date(props.time).toLocaleString()}</span> </div> </div> <> <p>{props.text}</p> </> <style jsx>{` .main-wrapper { display: flex; box-shadow: 0px 0px 12px #CCC; border-radius: 5px; border: 2px solid #55acee; flex-direction: column; } .user-wrapper { display: flex; flex-direction: column; align-items: flex-start; } img { object-fit: contain; } .user-info { display: flex; justify-content: space-around; flex-direction: column; align-items: flex-start; } p { max-width: 500px; margin: .25rem .6rem; font-size: 1.2rem; } span { margin-top:3px; margin-bottom: 3px; } div { margin: 5px; padding: 5px; } `}</style> </div> ) export default Twitter
Import all three of the files at the top of index.js
import React from 'react' import axios from 'axios' import Twitter from '../components/Twitter' // new import Giphy from '../components/Giphy' // new import Subreddit from '../components/Subreddit' // new ...
Then, convert the getData
function to the React hook useEffect
. Replace the current function code with,
... React.useEffect(() => { // fetches data from our api axios({ method: 'GET', url: '/api/graphql', }) .then(response => { // add data to state setData(response.data.data) setLoading(false) }) .catch(error => { setLoading(false) console.log(error) }) }, []) ....
This makes it so the API call is run whenever the component mounts. Consequently, we don’t have to hit a button to fetch data anymore.
Next, replace all the JSX code in index.js
with the JSX below:
<div> <h1>Using a GraqhQL Api with React.js</h1> {loading && <span>Data is loading...</span>} <main> <section id="reddit"> <h2>Subreddits</h2> <h3>Coronavirus</h3> {data && data.reddit.firstSubreddit.newListings.map(item => { return <Subreddit url={item.url} title={item.title} comments={item.comments} /> })} <h3>Politics</h3> {data && data.reddit.secondSubreddit.newListings.map(item => { return <Subreddit url={item.url} title={item.title} comments={item.comments} key={item.title} /> })} </section> <section id="twitter"> <h2>Twitter</h2> {data && data.twitter.search.map(tweet => { return <Twitter text={tweet.text} time={tweet.created_at} name={tweet.user.name} screen_name={tweet.user.screen_name} followers={tweet.user.followers_count} profileImageUrl={tweet.user.profile_image_url} key={tweet.id} /> })} </section> <section id="giphy"> <h2>Giphy</h2> {data.giphy && data.giphy.search && data.giphy.search.map(gif => { return <Giphy url={gif.images.fixed_width_downsampled.url} id={gif.id} key={gif.id} giphyUrl={gif.url} /> })} </section> </main> <style jsx>{` h2, h3 { display: block; width: auto; font-size: 2rem; padding: 5px 10px; margin: 5px; } h3 { font-size: 1.3rem; display: inline-block; background: black; color: white; } main { display: flex; } #giphy { text-align: center; } #giphy > h2 { background: #39ff14; color: black; border: 2px solid black; } #twitter > h2 { background: white; color: #55acee; border: 2px solid #55acee; } #reddit > h2 { background: #FF4301; color: white; border: 2px solid #FF4301; } h1 { font-family: monospace; text-align: center; font-size: 4rem; } `} </style> </div>
Finally, add one more state variable to tell the user that data is being fetched when the page first loads.
... export default function Index() { let [data, setData] = React.useState('') let [loading, setLoading] = React.useState(true) // new ...
Now, all that useful data is easier to read whenever the page loads!
The last section has a lot of unexplained code. Some of it is Next.js specific and some if React.js specific.
The styling is specific to Next.js projects and you can read more about it by following this link.
If the React.js code is a little difficult to follow I suggest checking out these React articles on RapidAPI or by following a beginner tutorial on reactjs.org.
Conclusion
In my opinion, the logical next step is to create a form where a user can enter search parameters that replace the hardcoded variable values. That would be a nice challenge for someone looking to advance the app a little further.
Regardless, let’s appreciate what we have done. That whole application was built with one query and one API route. Granted, the query was a little big but the one API route concept remains.
Having one endpoint or a single route is one of the big advantages of GraphQL. If you want to learn more about GraphQL you can check out the official documentation, or you can check out more of the GraphQL APIs on RapidAPI. I hope you enjoyed the example and article! Please leave comments or questions if you have any!
Jason says
it’s spelled graphql