If you’ve started using RTK Query for managing your API requests in Redux, you’ve definitely encountered the baseQuery option within your createApi setup. For most common scenarios involving standard REST APIs, you’re directed towards using fetchBaseQuery:
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const myApi = createApi({
reducerPath: 'myApi',
baseQuery: fetchBaseQuery({ baseUrl: '/api' }), // <-- You use fetchBaseQuery here
endpoints: (builder) => ({
// ... your endpoints ...
}),
});
This works like magic! It takes care of sending HTTP requests, handling responses, and fitting everything into RTK Query’s caching and state management layer.
But as you delve deeper, perhaps looking at advanced examples or exploring custom integrations, you might stumble upon documentation or type definitions mentioning BaseQueryFn. You might wonder: How does this relate to fetchBaseQuery? Are they different things? Do I ever use BaseQueryFn directly?
Let’s clear up the confusion. Understanding the distinction between these two is fundamental to grasping how RTK Query achieves its flexible abstraction over data fetching.
๐ง The Foundation: RTK Query’s BaseQueryFn Contract
RTK Query is designed to be data-fetching library agnostic. It doesn’t force you to use fetch or axios or anything else. To achieve this, it needs a standard way for any fetching mechanism to communicate back its results. This is where the concept of the “base query” comes in.
A Base Query Function is simply a function that RTK Query calls internally whenever an endpoint needs to fetch data. This function is responsible for:
- Taking the specific details of the request (like the URL path, HTTP method, body, parameters, etc.) as input.
- Executing the actual data fetching (e.g., making an HTTP call, reading from local storage, interacting with a WebSocket).
- Returning the result in a standardized format that RTK Query understands:
- For success: { data: yourParsedResponseBody }
- For error: { error: yourErrorObject } (This object typically includes details like status code, error message, or error response body, e.g., { status: 404, data: { message: ‘Not found’ } } or { error: ‘Network error’ }).
The BaseQueryFn you see in RTK Query’s type definitions is the TypeScript type (or interface, conceptually) that defines this contract. It’s the blueprint for what any function must look like in terms of arguments and return value to be compatible with RTK Query’s base query slot.
// Simplified conceptual view of the BaseQueryFn type:
type BaseQueryFn<
Args = any, // The type of argument passed to the query
Result = unknown, // Expected success response data type
Error = unknown // Expected error data type
> = (
args: Args, // The data provided by your endpoint's query definition
api: BaseQueryApi, // An object with helpers like getState, dispatch etc.
extraOptions: any // Options passed from the endpoint
) => Promise<{ data: Result } | { error: Error }>;
// It defines the shape of the function signature and its Promise result.
Think of BaseQueryFn as the job description or the interface. It says, “To be an RTK Query base query, you must be a function that accepts X and returns a Promise resolving to either a data shape Y or an error shape Z.”
๐ The Default Tool: fetchBaseQuery Implementation
While BaseQueryFn is the theoretical blueprint, you need a concrete function that follows this blueprint to actually make requests. This is where fetchBaseQuery comes in.
- What it is: fetchBaseQuery is a specific, pre-built implementation of a base query function provided by the @reduxjs/toolkit/query library.
- What it does: It wraps the browser’s native fetch API and adds all the necessary logic to fit the BaseQueryFn contract for typical REST API interactions.
- It takes configuration like baseUrl, default headers, etc., when you call it: fetchBaseQuery({ baseUrl: ‘/api’ }).
- The function that fetchBaseQuery returns is the actual base query function that will be used by createApi.
- This returned function takes the endpoint’s details, makes the fetch call, handles JSON serialization/deserialization, checks HTTP status codes, and formats the result into the required { data: … } or { error: … } shape.
// What happens conceptually when you call fetchBaseQuery:
function fetchBaseQuery(config) {
// It returns THIS function (which conforms to BaseQueryFn)
return async function actualBaseQueryFn(arg, api, extraOptions) {
// ... use 'config' (like baseUrl) and 'arg' (like endpoint path/method/body) ...
// ... internally call 'fetch' ...
// ... process the 'fetch' response ...
// ... RETURN either { data: ... } or { error: ... } as per the BaseQueryFn type
};
}
Think of fetchBaseQuery as a specific instance of the BaseQueryFn job description, built using fetch. It’s the ready-to-use tool you get with the library that does the fetching work according to the defined rules.
The Core Distinction: Contract vs. Implementation
The key difference is simple but crucial:
- BaseQueryFn is the abstract type/contract: It defines the interface, the required signature and return structure for a base query function. You rarely use BaseQueryFn itself in your runtime code; you primarily interact with its definition in TypeScript or conceptually when building a custom base query.
- fetchBaseQuery is a concrete implementation: It’s an actual factory function provided by RTK Query that produces a function conforming to the BaseQueryFn contract, using the fetch API under the hood. This is what you pass to the baseQuery option in createApi for most use cases.
fetchBaseQuery satisfies the BaseQueryFn contract. The function returned by fetchBaseQuery({…}) is a BaseQueryFn.
Why This Matters: Custom Base Queries
Understanding this difference is important because it reveals RTK Query’s flexibility. If your data fetching needs go beyond what fetchBaseQuery provides (e.g., you need to use axios, interact with a non-HTTP source, or require custom error handling logic across all endpoints), you can:
- Write your own function that implements the BaseQueryFn contract.
- Pass your custom function to the baseQuery option in createApi instead of calling fetchBaseQuery.
For example, a basic axios base query implementation would involve writing a function that takes the BaseQueryFn arguments, uses axios to make the request, and then formats axios’s response and error structures into the { data: … } or { error: … } format expected by BaseQueryFn.
In Summary
- BaseQueryFn: The blueprint. A type definition specifying the interface for any function that serves as an RTK Query base query. Defines the expected signature and return shape.
- fetchBaseQuery: A specific tool built by the library. A factory function that creates a concrete base query function using the fetch API, adhering to the BaseQueryFn contract.
You configure your API using fetchBaseQuery (the implementation) most of the time because it’s the convenient default. But the system works because fetchBaseQuery produces a function that conforms to the BaseQueryFn type (the contract), just as any custom base query function you might write must.
This abstraction allows RTK Query’s core logic to remain blissfully unaware of how the data was fetched, focusing instead on the state management and caching based on the standardized { data: … } or { error: … } result.
Happy querying!