Consuming GraphQL APIs in React

Consuming GraphQL APIs in React

Urql Hooks & Hasura Instant Cloud GraphQL API

ยท

5 min read

Featured on Hashnode

image.png 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

image.png

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.

image.png

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.

image.png

Select the created database and add an Animal table with 3 columns (id, name, image).

image.png

We insert a row through the GUI to then test in our project and we are ready to write some code ๐Ÿค“.

image.png

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 ๐Ÿถ!

image.png

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 https://www.buymeacoffee.com/snappy.guy

ย