Skip to main content

Command Palette

Search for a command to run...

Efficient API Consumption in React TypeScript

A Fun Dive into HTTP Clients and React Query

Updated
6 min read
Efficient API Consumption in React TypeScript
N
Senior Software Engineer with 5+ years of expertise crafting innovative products that make people's lives easier, now with AI.

APIs are playing an important role in software development today. In the world of cloud computing, no application can function without APIs. The way you architect and consume these APIs can significantly impact your app's performance.

Let’s explore building a robust HTTP Client in React TypeScript, integrating caching with Tanstack Query (formerly React Query), and creating custom hooks that streamline your API calls.

🌐 Understanding HTTP APIs

HTTP (HyperText Transfer Protocol) is the universal language systems use to communicate. Whether it’s fetching data, updating a resource, or deleting something from the server, HTTP has got us covered. Here’s a quick refresher on the HTTP methods:

  • GET: Grabs the data you need from the server.

  • POST: Sends data to create something new on the server.

  • PUT: Updates an existing resource entirely.

  • PATCH: Updates a part of an existing resource.

  • DELETE: Removes something from the server.

Each method is like a tool in your toolbox—pick the right one for the job, and your APIs will be a joy to work with.

💡 Why Axios?

So, why are we using Axios for our HTTP Client? Imagine having a personal assistant who handles all the boring tasks like adding headers, dealing with JSON, and managing timeouts—Axios does just that! It’s a library that simplifies HTTP requests, making our code cleaner and easier to maintain.

🛠️ Creating the HTTP Client

Step 1: Installing Axios

Before we dive into the fun stuff, let’s install Axios:

npm install axios

Step 2: Setting Up the HTTP Client

Now, let’s set up our HTTP Client to make API calls a breeze.

import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios";

class HttpClient {
  private instance: AxiosInstance;

  constructor(baseURL: string) {
    this.instance = axios.create({
      baseURL,
      withCredentials: true,
    });

    this.instance.interceptors.request.use(this.handleRequest);
    this.instance.interceptors.response.use(this.handleResponse);
  }

  private handleRequest = (config: AxiosRequestConfig): AxiosRequestConfig => {
    const token = localStorage.getItem("authToken"); // Replace with your token logic
    if (token) {
      if (!config.headers) {
        config.headers = {
          Authorization: `Bearer ${token}`,
        };
        return config;
      }

      config.headers["Authorization"] = `Bearer ${token}`;
    }
    return config;
  };

  private handleResponse = (response: AxiosResponse): AxiosResponse => {
    return response;
  };

  public get<T>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<AxiosResponse<T>> {
    return this.instance.get<T>(url, config);
  }

  public post<T>(
    url: string,
    data: any,
    config?: AxiosRequestConfig
  ): Promise<AxiosResponse<T>> {
    return this.instance.post<T>(url, data, config);
  }

  public put<T>(
    url: string,
    data: any,
    config?: AxiosRequestConfig
  ): Promise<AxiosResponse<T>> {
    return this.instance.put<T>(url, data, config);
  }

  public patch<T>(
    url: string,
    data: any,
    config?: AxiosRequestConfig
  ): Promise<AxiosResponse<T>> {
    return this.instance.patch<T>(url, data, config);
  }

  public delete<T>(
    url: string,
    config?: AxiosRequestConfig
  ): Promise<AxiosResponse<T>> {
    return this.instance.delete<T>(url, config);
  }
}

const httpClient = new HttpClient("https://jsonplaceholder.typicode.com");
export default httpClient;

Create a User Model

export interface Address {
  street: string;
  suite: string;
  city: string;
  zipcode: string;
  geo: {
    lat: string;
    lng: string;
  };
}

export interface Company {
  name: string;
  catchPhrase: string;
  bs: string;
}

export interface User {
  id: number;
  name: string;
  username: string;
  email: string;
  address: Address;
  phone: string;
  website: string;
  company: Company;
}

🔄 Handling API Requests

Creating a User

const createUser = async (userData: UserData) => {
  const response = await httpClient.post<User>('/users', userData);
  return response.data;
};

Fetching Users

const fetchUsers = async () => {
  const response = await httpClient.get<User[]>('/users');
  return response.data;
};

Updating a User

const updateUser = async (userId: string, userData: UserData) => {
  const response = await httpClient.put<User>(`/users/${userId}`, userData);
  return response.data;
};

Deleting a User

const deleteUser = async (userId: string) => {
  const response = await httpClient.delete(`/users/${userId}`);
  return response.data;
};

🗄️ The Power of Caching with Tanstack Query

When consuming APIs, one of the biggest challenges is managing the state of the data—especially when it comes to caching. Caching allows us to store responses so that repeated requests for the same data can be served faster, improving the overall performance of our application. This is where Tanstack Query comes in. Tanstack Query is a powerful data-fetching library that makes it simple to manage server-state in your React apps, with built-in caching, synchronization, and more.

Installation

To get started, install Tanstack Query:

npm install @tanstack/react-query

⚙️ Creating Custom Hooks for API Requests

Creating custom hooks makes your API calls reusable and easier to manage. For each type of request (fetch, create, update, delete), we'll create a custom hook using Tanstack Query and the HttpClient we built earlier.

Setting Up Query Keys

First, let's define some constants for our query keys:

// queryKeys.ts
export const QUERY_KEYS = {
  USERS: 'users',
  USER: (userId: string) => ['user', userId],
};

Custom Hooks Examples

Fetching Users

import { useQuery } from '@tanstack/react-query';
import httpClient from '../api/HttpClient';
import { QUERY_KEYS } from '../api/queryKeys';
import { User } from '../api/interfaces';

const useFetchUsers = () => {
  return useQuery<User[]>({
    queryKey: [QUERY_KEYS.USERS],
    queryFn: () => httpClient.get<User[]>("/users").then((res) => res.data),
  });
};

Creating a User

import { useMutation, useQueryClient } from '@tanstack/react-query';
import httpClient from '../api/HttpClient';
import { QUERY_KEYS } from '../api/queryKeys';
import { User } from '../api/interfaces';

const useCreateUser = () => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (newUser: Omit<User, "id">) =>
      httpClient.post<User>("/users", newUser),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USERS] });
    },
  });
};

Updating a User

import { useMutation, useQueryClient } from '@tanstack/react-query';
import httpClient from '../api/HttpClient';
import { QUERY_KEYS } from '../api/queryKeys';
import { User } from '../api/interfaces';

const useUpdateUser = (userId: number) => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: (updatedUser: Partial<Omit<User, "id">>) =>
      httpClient.put<User>(`/users/${userId}`, updatedUser),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USERS] });
      queryClient.invalidateQueries({
        queryKey: [QUERY_KEYS.USER(userId.toString())],
      });
    },
  });
};

Deleting a User

import { useMutation, useQueryClient } from '@tanstack/react-query';
import httpClient from '../api/HttpClient';
import { QUERY_KEYS } from '../api/queryKeys';

const useDeleteUser = (userId: number) => {
  const queryClient = useQueryClient();

  return useMutation({
    mutationFn: () => httpClient.delete(`/users/${userId}`),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.USERS] });
    },
  });
};

Consumption of Hooks!

Here's a sample of how you can consume those hooks. We're using jsonplaceholder for mock APIs. You can see it in HttpClient file.


const App: React.FC = () => {
  const { data: users } = useFetchUsers();
  const createUser = useCreateUser();
  const updateUser = useUpdateUser(1); // Example user ID
  const deleteUser = useDeleteUser(1); // Example user ID

  return (
      <div>
        <h1>Users</h1>
        <button onClick={() => createUser.mutate(newUser)}>Create User</button>
        <button
          onClick={() =>
            updateUser.mutate({
              name: "Updated User",
            })
          }
        >
          Update User
        </button>
        <button onClick={() => deleteUser.mutate()}>Delete User</button>

        <ul>
          {users?.map((user: User) => (
            <li key={user.id}>
              {user.id} - {user.name}
            </li>
          ))}
        </ul>
      </div>
  );
};

📦 Wrapping It Up

Efficiently consuming APIs is crucial for building performant and scalable applications. By setting up a robust HTTP Client with Axios, handling state with Tanstack Query, and creating custom hooks for each API request, you’ll be well on your way to mastering API consumption in React TypeScript.

Remember, the key is to keep your code modular and reusable. As your application grows, these practices will help you maintain a clean and efficient codebase.

C

HIRE DIGITAL HACK RECOVERY FOR BEST STOLEN CRYPTO RECOVERY SERVICES ONLINE

Navigating the complexities of legal practice involves a great deal of precision and care, and I had always been diligent about securing my financial assets. Despite this, I found myself in a distressing predicament when I lost access to my Bitcoin wallet containing $300,000. The problem began when a computer crash wiped out my encrypted wallet file, leaving me unable to retrieve my funds. The situation was particularly dire as the money was crucial not only for personal savings but also for maintaining the financial stability of my practice .The initial panic was overwhelming. Despite my usual preparedness for any legal or financial issue, this felt like an insurmountable obstacle. I explored every possible solution, from consulting IT specialists to trying various recovery tools, but to no avail. My frustration grew as the days passed with no progress .During my search for help, I discovered DIGITAL HACK RECOVERY. Although I was initially skeptical, the gravity of the situation pushed me to give them a chance. From our first interaction, I was impressed by their professionalism and empathy. They understood the urgency of my case and were transparent about the recovery process. What stood out was their comprehensive approach. DIGITAL HACK RECOVERY didn’t just focus on recovering my wallet; they also provided valuable advice on enhancing my digital security. They introduced me to advanced encryption techniques and secure backup practices that I hadn’t previously considered. This not only helped in recovering my lost Bitcoin but also strengthened my overall financial security. The waiting period was fraught with anxiety and uncertainty. However, DIGITAL HACK RECOVERY kept me informed and reassured throughout the process. When they successfully recovered my wallet, the relief was immense. I felt a tremendous weight lifted off my shoulders and regained confidence in managing financial setbacks. This experience underscored the importance of having a robust backup plan and the right tools in place, no matter how prepared you think you are. Thanks to DIGITAL HACK RECOVERY, I not only regained access to my funds but also gained a deeper understanding of digital asset security. Their expertise and support were truly invaluable, and I wholeheartedly recommend their services to anyone facing similar challenges. If you ever find yourself in a bind, DIGITAL HACK RECOVERY is the team you need on your side .Reach out to DIGITAL HACK RECOVERY via their contact WhatsApp +19152151930

https :// digital hack recovery . com

digital hack recovery @ techie . com

More from this blog

N

Navayuvan's Blog

9 posts

I write about building real-world software with AI, focusing on what broke, what scaled, and what finally worked, so you can design workflows that fit your own engineering context.