ReactHustle

How to use Axios in NextJS using axios-hooks Package

Jasser Mark Arioste

Jasser Mark Arioste

How to use Axios in NextJS using axios-hooks Package

Hello, hustler! In this tutorial, you'll learn how to use axios on the client-side in NextJS using the axios-hooks package. We'll also use typescript throughout the tutorial.

Introduction #

In every production web app, we always need some kind of HTTP client to handle requests and axios is a really good HTTP client with a lot of features. 

I checked the currently available axios hooks libraries such as axios-hooks and use-axios-client. I found out that axios-hooks has a really good implementation for React. One problem I found is that it's a bit tricky to set up SSR with NextJS since it has an opinionated way of setting up SSR. Also, most of its examples of SSR are also done using express.

In this tutorial, you'll learn how to install and use axios-hooks and set up SSR with it in NextJS.

Installing the Dependencies #

First, let's install axios and axios-hooks package by running the command:

yarn add axios axios-hooks
1

Now, we're ready to make some requests on the browser.

How to do a GET Request #

To call a GET request, all we have to do is to use the useAxios hook. We can pass an AxiosRequestConfig object to customize the request:

// pages/users.tsx
import useAxios from "axios-hooks";
// define UserResponse for type-safty
type User = {
  id: string;
  firstName: string;
  age: number;
};

interface UserResponse {
  users: User[];
  total: number;
  skip: number;
  limit: number;
}

const UsersPage = () => {
  //use the useAxios hook and pass the AxiosRequestConfig
  const [{ data, loading, error }] = useAxios<UserResponse>({
    baseURL: "https://dummyjson.com",
    url: "/users?skip=0&limit=5&select=firstName,age",
  });

  return (
    <div>
      <h1>Users Page</h1>
      {loading && <p>Loading...</p>}
      {!!error && <p>{error.message}</p>}
      {!!data && <pre>{JSON.stringify(data, null, 4)}</pre>}
    </div>
  );
};
export default UsersPage;
123456789101112131415161718192021222324252627282930313233

It uses the useEffect hook internally to call the request once the component is rendered and gives us the loading, error and data states. 

If you want to delay or have more control over the timing of the call, you can use the {manual:true} option:

// pages/users.tsx
const UsersPage = () => {
  //useAxios returns a 2nd element to refetch the array
  const [{ data, loading, error }, refetch] = useAxios<UserResponse>(
    {
      baseURL: "https://dummyjson.com",
      url: "/users?skip=0&limit=5&select=firstName,age",
    },
    // use manual:true so that it doesn't run on the first render
    {
      manual: true,
    }
  );

  return (
    <div>
      <h1>Users Page</h1>
      <button onClick={() => refetch()}>Load Users</button>
      {loading && <p>Loading...</p>}
      {!!error && <p>{error.message}</p>}
      {!!data && <pre>{JSON.stringify(data, null, 4)}</pre>}
    </div>
  );
};
123456789101112131415161718192021222324

Here's the output:

useAxios manual true

The { manual:true } option is especially useful if you're using a POST/PUT request.

More information about this can be found in its documentation on GitHub: simoneb/axios-hooks

Implementing axios-hooks SSR for NextJS #

I had some trouble implementing SSR for axios-hooks since there are currently no examples for NextJS. I was able to get around it by digging into the axios-hooks code. If you have the same problem as me, I think this will help you a ton.

How axios-hooks does SSR normally

The axios-hooks package does server-side rendering (SSR) by first rendering the app. As it renders the app, all the requests will be stored in an LRU cache. Next, it renders the cache contents using serializeCache. Then load the cache in the browser using the loadCache once it React hydrates the page.

The Problem in NextJS

The above method does not work In NextJS because we don't have the opportunity to call React.hydrateRoot since this is done internally by NextJS. We don't have a place to call serializeCache.

Solving the Problem

Here are  the steps of how I solved this problem which turned out to be pretty simple:

  1. Create a cache data exactly the same as axios-hooks in getStaticProps or getServerSideProps
  2. Pass the cache data to the props
  3. use loadCache in _app.tsx

First, let's create a serializeResponse function that mimics seralizeCache. Create the file lib/serializeResponse.ts

// lib/serializeResponse.ts
import { AxiosRequestConfig, AxiosResponse } from "axios";
// the cached item for axios hooks have this structure
type CacheItem = [string, { value: any }];

// use the config and the response to create a cached item
export function serializeResponse(
  config: AxiosRequestConfig,
  response: AxiosResponse<any, any>
): CacheItem {
  const key = JSON.stringify({ ...config });
  return [
    key,
    {
      value: {
        data: response.data,
        headers: {
          ...response.headers,
        },
        status: response.status,
        statusText: response.statusText,
      },
    },
  ];
}
12345678910111213141516171819202122232425

Here, we're just creating cached data similar to how axios-hooks does it.

Next, let's use getServerSideProps to execute the request on the server and pass data to the client:

import axios, { AxiosRequestConfig } from "axios";
import useAxios from "axios-hooks";
import { GetStaticProps } from "next";
import { serializeResponse } from "../lib/serializeResponse";
// define UserResponse for type-safety
type User = {
  id: string;
  firstName: string;
  age: number;
};

interface UserResponse {
  users: User[];
  total: number;
  skip: number;
  limit: number;
}

const UsersPage = () => {
  //useAxios returns a 2nd element to refetch the array
  const [{ data, loading, error }, refetch] = useAxios<UserResponse>({
    baseURL: "https://dummyjson.com",
    url: "/users?skip=0&limit=5&select=firstName,age",
  });

  return (
    <div>
      <h1>Users Page</h1>
      <button onClick={() => refetch()}>Load Users</button>
      {loading && <p>Loading...</p>}
      {!!error && <p>{error.message}</p>}
      {!!data && <pre>{JSON.stringify(data, null, 4)}</pre>}
    </div>
  );
};
export default UsersPage;

export const getServerSideProps: GetStaticProps = async () => {
  // axios config, must be exactly the same as the one used in useAxios hook so that it creates the same key.
  const config: AxiosRequestConfig = {
    baseURL: "https://dummyjson.com",
    url: "/users?skip=0&limit=5&select=firstName,age",
  };
  // execute the http request
  const response = await axios(config);

  // serialize the response
  return {
    props: {
      __CACHE__: [serializeResponse(config, response)],
    },
  };
};
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253

Lastly, on _app.tsx, we have to call loadCache.

// pages/_app.tsx
import "../styles/globals.css";
import type { AppProps } from "next/app";
import { loadCache } from "axios-hooks";
function MyApp({ Component, pageProps }: AppProps) {
  loadCache(pageProps.__CACHE__ ?? []);
  return <Component {...pageProps} />;
}
export default MyApp;
123456789

When we check the network tab, there are no calls to dummy JSON API on the client side:

use axios ssr next js network tab

How to do POST/PUT Request #

To use POST/PUT request, you may pass the { method: "POST" } option and { manual: true}. For example, suppose we are implementing a signup page:

// pages/signup.tsx
import useAxios from "axios-hooks";
type SignUpResponse = {
  success: boolean;
};
const SignupPage = () => {
  const [{ data, loading, error }, signup] = useAxios<SignUpResponse>(
    {
      url: "/api/signup",
      method: "POST",
    },
    {
      manual: true,
    }
  );

  return (
    <div style={{ padding: 16 }}>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          signup();
        }}
      >
        <h1>Sign Up</h1>
        <input name="username" />
        <input name="password" />
        {error && <p>{error.message}</p>}
        {data?.success && <p>Successfully signed up</p>}
        <div>
          <button>{loading ? "Loading..." : "Sign up"}</button>
        </div>
      </form>
    </div>
  );
};
export default SignupPage;

12345678910111213141516171819202122232425262728293031323334353637

We use the second element returned by the useAxios hook to manually call the request on form submission. Then we use the error, data, and loading states to reflect the status of the request to the UI.

Full Code and Demo  #

The full code is available on GitHub: jmarioste/nextjs-axios-hooks-tutorial

The demo is available on Stackblitz: nextjs-axios-hooks-tutorial

Conclusion #

We learned how to use axios with NextJS using the axios-hooks library with Typescript. We also learned how to work around its SSR limitations with NextJS. To be honest, I think there are better HTTP client libraries for NextJS like swr and react-query. For example, SWR has a much easier API for implementing SSR in NextJS (they have examples), it also caching feature similar to axios-hooks and it's only 4kb in bundle size. When you use axios and axios-hooks, it adds up to 18kb in total.

If you like this tutorial, please leave a like or share this article. For future tutorials like this, please subscribe to our newsletter or follow me on Twitter.

Resources #

Credits: Image by Анатолий Стафичук from Pixabay

Share this post!

Related Posts

Disclaimer

This content may contain links to products, software and services. Please assume all such links are affiliate links which may result in my earning commissions and fees.
As an Amazon Associate, I earn from qualifying purchases. This means that whenever you buy a product on Amazon from a link on our site, we receive a small percentage of its price at no extra cost to you. This helps us continue to provide valuable content and reviews to you. Thank you for your support!
Donate to ReactHustle