Consuming GraphQL APIs in React
Urql Hooks & Hasura Instant Cloud GraphQL API
React is the most popular Frontend Framework (library) for obvious reasons, it has a great community with many ready made packages to solve every task and allows for fast component driven development without the hassle of copy-pasting code.
React doesn't provide an inhouse solution for API requests nor a certain guideline to follow other than to use the common component lifecycle methods which are now covered through hooks. This means it's up to developers to decide what 's the best way to consume an API whether it be REST, GraphQL or some shady FIX API.
We are going to build a fast & reliant GraphQL API with Hasura, consume it in the most popular frontend library React and showcase the cutest animals out there dogs.
1. Why GraphQL
Basically it's fast. Extremely fast development, out of the box API documentation and option to request exactly the amount of resources you need or to batch multiple requests into one. It's been initially developed by Facebook and then continued as a community project therefore it's perfect to use in another Facebook developed tech such as React.
2. What is Hasura
Cloud service to host GraphQL API, provides speed, increases API development and has built-in Auth & Caching. It has a very friendly GUI and also a free plan to test it's capabilities.
Like other cloud providers it asks us to select a pricing plan, server location and project name and then we can launch the console.
To be extremely fast we'll configure our database through Heroku, just follow the steps and we have a free playground to build our API.
Select the created database and add an Animal table with 3 columns (id, name, image).
We insert a row through the GUI to then test in our project and we are ready to write some code ๐ค.
If we want to play around with queries or mutations we can always use the GraphiQL Interface which gives instant documentation for queries or mutations we could make.
3. Urql
A Formidable (excuse the pun) open source lightweight library created for consuming GraphQL APIs in React, Svelte, Vue, or even vanilla JavaScript projects. Although no one stops us from using something like axios, react-query or even the browser based fetch API (or other GraphQL solutions like Apollo or Relay but check here for more info), I believe urql will boost development speed and hide the complexity of parsing, making queries & mutations.
To get started with urql & graphql we run on a previously created NextJS project:
npm install urql graphql
// or
yarn add urql graphql
Wrap the App root component in the urql Provider and set the options as required.
import "@shared/globals.css";
import React from "react";
import { createClient, Provider } from "urql";
const client = createClient({
url: "replace with Hasura url or other Graphql API url",
fetchOptions: () => {
return {
headers: { "x-hasura-admin-secret": "your hasura secret" },
};
},
});
const App = ({ Component, pageProps }) => {
return (
<Provider value={client}>
<Component {...pageProps} />
</Provider>
);
};
export default App;
We can now use the useQuery and useMutations hooks from urql to quickly access the GraphQL Hasura hosted API like this:
import { Fragment, useState } from "react";
import { useMutation, useQuery } from "urql";
const GetDogs = `
query {
animals {
id
name
image
}
}
`;
const AddDog = `
mutation ($name: String!, $image: String!) {
insert_animals_one(object: {name: $name, image: $image}) {
image
name
}
}
`;
const Home = () => {
const [formData, setFormData] = useState(null);
const [result, reexecuteQuery] = useQuery({
query: GetDogs
});
const [addDogResult, addDog] = useMutation(AddDog);
const { data, fetching, error } = result;
const animals = data?.animals ?? [];
const handleChange = (event) => {
setFormData({ ...formData, [event.target.name]: event.target.value });
};
const handleSubmit = (event) => {
// get random dog image
event.preventDefault();
try {
fetch("https://dog.ceo/api/breeds/image/random")
.then((response) => response.json())
.then(({ message: image }) => {
// send image and name to mutation
addDog({ ...formData, image });
});
} catch (err) {
console.log(err);
}
};
return (
<Fragment>
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
placeholder="Enter animal name"
onChange={handleChange}
/>
<button type="submit">Add</button>
</form>
{fetching && <p>Loading...</p>}
{!fetching && (
<div>
{animals.map((a) => (
<div key={a.id}>
<p>{a.name}</p>
<img src={a.image} alt={"Animal " + a.name} />
</div>
))}
</div>
)}
</Fragment>
);
};
export default Home;
After adding some TailwindCSS classes for minimal styling we get
import { Fragment, useState } from "react";
import { useMutation, useQuery } from "urql";
const GetDogs = `
query {
animals {
id
name
image
}
}
`;
const AddDog = `
mutation ($name: String!, $image: String!) {
insert_animals_one(object: {name: $name, image: $image}) {
image
name
}
}
`;
const Home = () => {
const [formData, setFormData] = useState(null);
const [result, reexecuteQuery] = useQuery({
query: GetDogs
});
const [addDogResult, addDog] = useMutation(AddDog);
const { data, fetching, error } = result;
const animals = data?.animals ?? [];
const handleChange = (event) => {
setFormData({ ...formData, [event.target.name]: event.target.value });
};
const handleSubmit = (event) => {
// get random dog image
event.preventDefault();
try {
fetch("https://dog.ceo/api/breeds/image/random")
.then((response) => response.json())
.then(({ message: image }) => {
// send image and name to mutation
addDog({ ...formData, image });
});
} catch (err) {
console.log(err);
}
};
return (
<Fragment>
<form
onSubmit={handleSubmit}
className="flex flex-col w-1/4 ml-4 mt-4 gap-2"
>
<input
className="border border-gray-200 rounded px-4 py-1"
type="text"
name="name"
placeholder="Enter animal name"
onChange={handleChange}
/>
<button
type="submit"
className="px-4 py-1 rounded text-white bg-indigo-500 hover:bg-indigo-400 focus:bg-indigo-600"
>
Add
</button>
</form>
{fetching && <p>Loading...</p>}
{!fetching && (
<div className="flex ml-4 mt-6">
{animals.map((a) => (
<div key={a.id} className="mr-4">
<p className="font-bold mb-2">{a.name}</p>
<img
src={a.image}
alt={"Animal " + a.name}
width="150px"
height="150px"
className="rounded"
/>
</div>
))}
</div>
)}
</Fragment>
);
};
export default Home;
Happy Furry Fiesta ๐ถ!
A simple, fully working example on making GraphQL queries and mutations with Urql in React.
There are many more options such as persistence, SSR, debugging, Auth and testing that can be configured for urql but that's not the purpose of this simple example on how to quickly consume an API in React.
If you want to check this demo project (replace api_url & secret in _app.jsx) here is the CodeSandbox.
I hope you enjoyed this short tutorial for consuming GraphQL in React and would love if you give it a ๐ฆ!
If you want to support my endeavours in the development world check my buymeacoffee