React Query

Learn how to use CallApi with React Query

This guide covers essential patterns and best practices for using CallApi with React Query, combining CallApi's features with React Query's caching and synchronization capabilities.

Quick Start

By default, CallApi returns errors as values in the result object instead of throwing them, which is great for explicit error handling. However, React Query expects query functions to either return data on success or throw an error on failure.

To make CallApi work seamlessly with React Query, configure it to throw errors and return only the data:

api/todos.ts
import { useQuery } from "@tanstack/react-query";
import { callApi } from "@zayne-labs/callapi";

type Todo = {
	completed: boolean;
	id: number;
	title: string;
};

export const useTodos = () => {
	return useQuery({
		queryKey: ["todos"],
		queryFn: () => {
			return callApi<Todo[], false>("/todos", {
				throwOnError: true,
				resultMode: "onlyData",
			});
		},
	});
};

Configuration Options

Key options for React Query integration:

  • throwOnError: true - Makes CallApi throw errors instead of returning them
  • resultMode: "onlyData" - Returns just the data property, perfect for React Query

These settings ensure CallApi behaves exactly like React Query expects: throwing errors for failures and returning clean data for successes.

Centralized Configuration

api/client.ts
import { createFetchClient } from "@zayne-labs/callapi";

export const callApiForQuery = createFetchClient({
	baseURL: "https://api.example.com",
	// Default to React Query compatible settings
	throwOnError: true,
	resultMode: "onlyData",
});

// Use the configured client in queries
export const useTodos = () => {
	return useQuery({
		queryKey: ["todos"],
		queryFn: () => callApiForQuery<Todo[]>("/todos"),
	});
};

Data Inference via typescript

Using validation libraries like Zod provides both runtime safety and automatic type inference:

hooks/useTodos.ts
import { useQuery } from "@tanstack/react-query";
import { callApi } from "@zayne-labs/callapi";
import { z } from "zod";

const todosSchema = z.array(
	z.object({
		id: z.number(),
		title: z.string(),
		completed: z.boolean(),
	})
);

export const useTodos = () => {
	return useQuery({
		queryKey: ["todos"],
		queryFn: () => {
			return callApi("/todos", {
				schema: { data: todosSchema },
				throwOnError: true,
				resultMode: "onlyData",
			});
		},
	});
};

Option 2: Manual Type Specification

hooks/useTodos.ts
import { useQuery } from "@tanstack/react-query";
import { callApi } from "@zayne-labs/callapi";

type Todo = {
	completed: boolean;
	id: number;
	title: string;
};

export const useTodos = () => {
	return useQuery({
		queryKey: ["todos"],
		queryFn: () => {
			// Pass `false` as second generic to signal errors will be thrown allow callApi to return the expected type
			// This is needed due to TypeScript limitations with partial generic inference
			// See: https://github.com/microsoft/TypeScript/issues/26242
			return callApi<Todo[], false>("@get/todos", {
				throwOnError: true,
				resultMode: "onlyData",
			});
		},
	});
};
Edit on GitHub

Last updated on

On this page