Icon

Charles Crete

React Query + Axios for authentication

November 30, 2020

React Query is a great library. It provides an API similar to the Apollo GraphQL client, but in a backend-agnostic design. It is perfect for use with traditional REST APIs, or any type of data API you wish to fetch from.

Axios is a small and simple Promise-based JavaScript HTTP client for browsers and Node. It provides a simple API with powerful features such as automatic transforms for JSON data, and interceptors (both of which we'll be using in this post).

Today we'll be looking at integrating React Query and Axios in a React application. This can work with Next.js or create-react-app, or any React installation. The snippets will be using TypeScript but can be used as JavaScript by simply removing the typings.

Axios Context & provider

We'll start with setting up React Context for providing our components/Hooks an Axios instance with an interceptor that attaches our authentication.

First, let's create a Context for Axios:

export const AxiosContext = createContext<AxiosInstance>(undefined);

We'll also define the provider for our Context:

export default function AxiosProvider({
  children,
}: React.PropsWithChildren<unknown>) {
  const axios = useMemo(() => {
    const axios = Axios.create({
      headers: {
        "Content-Type": "application/json",
      },
    });

    axios.interceptors.request.use((config) => {
      // Read token for anywhere, in this case directly from localStorage
      const token = localStorage.getItem("token");
      if (token) {
        config.headers.Authorization = `Bearer ${token}`;
      }

      return config;
    });

    return axios;
  }, []);

  return (
    <AxiosContext.Provider value={axios}>{children}</AxiosContext.Provider>
  );
}

We'll also define a Hook for accessing our Axios instance:

export function useAxios() {
  return useContext(AxiosContext);
}

Make sure to wrap your application with the AxiosProvider (and ReactQueryCacheProvider) at the top-level. This can be done inside your _app page in Next.js:

// This should be at the top-level, outside your component
const queryCache = new QueryCache()

// ...

<AxiosProvider>
  <ReactQueryCacheProvider queryCache={queryCache}>
    {/* ... */}
  </ReactQueryCacheProvider>
</AxiosProvider>

(see React Query's quick start for more information)

Queries

Now that we are set up with our Axios provider and our React Query cache provider, we can create queries! I prefer creating a custom Hook for each query, allowing me to easily reuse it. My usual location for storing query Hooks are grouped inside src/hooks. For example, all project-related Hooks would be placed at src/hooks/projects.ts.

Finally, let's create a query:

export function useProjects() {
  // Get our Axios instance from our previously created Hook
  const axios = useAxios();

  // Create a query with the key `projects`
  return useQuery("projects", async () => {
    // Fetch data from our API using Axios. We'll talk about the typing below
    const { data } = await axios.get<IFetchAllProjectsResponse>(
      "/api/projects"
    );

    // Return the data from the Axios response
    return data.projects;
  });
}

If your back-end is TypeScript, I would highly recommend sharing the types for the API responses with your front-end (IFetchAllProjectsResponse in this case). This can provide type safety across the network layer, and avoid common mistakes with data responses.

Conclusion

Setting up React Query with Axios is an easy process, and I recommend this pattern. It provides an easy and consistent way to write queries with authentication.

About me

I am Charles Crete, I work with JavaScript/TypeScript (Node, React), Flutter, Go, and many other technologies.

Follow me on Twitter!