I'm pretty new to using adapters and RTK Query, and also not too advanced with TS, so sorry if the answer seems obvious! Everything is working fine when using queryFn
with firebase and using the response data through my app, but I'm trying to use createEntityAdapter
and keep running into TS issues and the overall logic not working correctly.
Here's the API I'm creating to retrieve all of the previous workouts from firestore. I've tried just returning what I have in the transformResponse
method inside of queryFn
, but that throws an error since it's expecting the result formatted similar to {data: data}
. I've read in other places you can't use queryFn
and transformResponse
together since transformResponse
is used to transform the data received from the query
method and queryFn
allows you to do this. Assuming there's an obvious approach with queryFn
but I'm not knowledgable enough to see it.
export const workoutApi = createApi({
baseQuery: fakeBaseQuery<WorkoutError>(),
endpoints: (build) => ({
getWorkouts: build.query<EntityState<Workout, string>, void>({
queryFn: async () => {
try {
const workouts = await firestore().collection('workouts').get();
const data = workouts.docs.map((doc) => ({
id: doc.id,
name: doc.data().name,
date: format(doc.data().date.toDate(), 'MM/dd/yyyy'),
}));
return { data };
} catch (e) {
return {
error: {
status: 500,
message: (e as Error).message,
},
};
}
},
transformResponse(res: Workout[]) {
return workoutsAdapter.setAll(initialState, res);
},
}),
}),
});
I get 2 typescript errors:
The first is on the queryFn
method:
Type '() => Promise<{ data: { id: string; name: any; date: string; }[]; error?: undefined; } | { error: { status: number; message: string; }; data?: undefined; }>' is not assignable to type '(arg: void, api: BaseQueryApi, extraOptions: {}, baseQuery: (arg: void) => MaybePromise<QueryReturnValue<unique symbol, WorkoutError, {}>>) => MaybePromise<...>'. Type 'Promise<{ data: { id: string; name: any; date: string; }[]; error?: undefined; } | { error: { status: number; message: string; }; data?: undefined; }>' is not assignable to type 'MaybePromise<QueryReturnValue<EntityState<Workout, string>, WorkoutError, {} | undefined>>'.
The second is on the transformResponse
method:
Type '(res: Workout[]) => EntityState<Workout, string>' is not assignable to type 'undefined'.
I might just be doing this completely wrong, but all of the examples I've found use the standard query
method instead of queryFn
.
Any help is really appreciated! Maybe it's just not possible using queryFn
, but at this point I'm lost.
I've read in other places you can't use
queryFn
andtransformResponse
together sincetransformResponse
is used to transform the data received from thequery
method andqueryFn
allows you to do this.
This is correct. If you check the transformResponse documentation the first thing it states is:
(optional, not applicable with
queryFn
)
Trying to use transformResponse
is not intended to work when using the queryFn
.
I think you are attempting to merge two completely separate concepts, i.e. RTK-Query caching query results vs Entity Adapters data structures for CRUD operations. The queryFn
and its return value is simply used to return a value to the query cache. If you are also needing to update an entity adapter with the result value then I suspect that using onQueryStarted
is what you are looking for. It's effectively an API endpoint "side-effect", e.g. when the query resolves you can issue side-effects to dispatch additional actions, or in your case update an entity adapter.
Example:
export const workoutApi = createApi({
baseQuery: fakeBaseQuery<WorkoutError>(),
endpoints: (build) => ({
// Update endpoint return type to match what you are returning.
// Here I'm assuming it is an array of Workout objects.
getWorkouts: build.query<Workout[], void>({
queryFn: async () => {
try {
const workouts = await firestore().collection('workouts').get();
const data = workouts.docs.map((doc) => ({
id: doc.id,
name: doc.data().name,
date: format(doc.data().date.toDate(), 'MM/dd/yyyy'),
}));
return { data };
} catch (e) {
return {
error: {
status: 500,
message: (e as Error).message,
},
};
}
},
onQueryStarted: async (arg, { queryFulfilled }) => {
try {
// data is Workout[] from queryFn
const { data } = await queryFulfilled;
workoutsAdapter.setAll(initialState, data);
} catch(e) {
// catch and handle/ignore error
}
},
}),
}),
});
Since you can't use queryFn
and transformResponse
together another suggestion might be to directly return the transformed entity data from the query function, i.e. something like return { data: workoutsAdapter.setAll(initialState, data) };.
export const workoutApi = createApi({
baseQuery: fakeBaseQuery<WorkoutError>(),
endpoints: (build) => ({
getWorkouts: build.query<Workout[], void>({
queryFn: async () => {
try {
const workouts = await firestore().collection('workouts').get();
const data = workouts.docs.map((doc) => ({
id: doc.id,
name: doc.data().name,
date: format(doc.data().date.toDate(), 'MM/dd/yyyy'),
}));
return {
data: workoutsAdapter.setAll(initialState, data)
};
} catch (e) {
return {
error: {
status: 500,
message: (e as Error).message,
},
};
}
},
}),
}),
});