I have two APIs. One is provided by Apple and one by my own server. In order to get the JWT token for the WeatherKitAPI provided by Apple I need to contact my server. I implemented an API for that. However, I need to have this data in the prepareHeaders of my other createApi, which I don't know how to implement:
General API
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const generalApi = createApi({
reducerPath: 'api',
tagTypes: ['settings' ],
baseQuery: fetchBaseQuery({
baseUrl: 'https://api.myapp.com/v1/',
prepareHeaders: (headers, { getState })=> {
const token = getState().session.token;
if (token) {
headers.set('authorization', `Bearer ${token}`);
}
return headers;
}
}),
endpoints: (builder)=> ({
getWeatherKitToken: builder.query({
query: ()=> 'getWeatherKitToken',
providesTags: [ 'settings' ]
}),
login: builder.mutation({
query: (body)=> ({
url: 'login',
method: 'POST',
body: body
}),
invalidatesTags: [ 'settings' ]
}),
}),
});
export const {
useGetWeatherKitTokenQuery,
useLoginMutation,
} = generalApi;
WeatherKit API
import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';
export const weatherKitApi = createApi({
reducerPath: 'weatherKit',
baseQuery: fetchBaseQuery({
baseUrl: 'https://weatherkit.apple.com/api/v1/',
prepareHeaders: (headers, {})=> {
/* Implement logic to get token here */
return headers;
}
}),
endpoints: (builder)=> ({
availability: builder.query({
query: ({ lat, lon })=> `availability/${lat}/${lon}`,
})
})
});
export const {
useAvailabilityQuery
} = weatherKitApi;
I tried to implement the useGetWeatherKitTokenQuery directly inside the prepareHeaders but this is unfortunately not allowed.
prepareHeaders: async (headers, { })=> {
const { data: token } = await useGetWeatherKitTokenQuery();
if (token) {
headers.set('authorization', `Bearer ${token}`);
}
return headers;
}
so first of prepeareHeaders
function, runs before every
request happens on current api
. So implementing query inside prepeareHeaders
gonna mean that you are going to run this function before every api request you do.
To make it work like that you are going to do it like so:
baseQuery: (args, { signal, dispatch, getState }, extraOptions, ...additionalArgs)=> fetchBaseQuery({
baseUrl: 'https://weatherkit.apple.com/api/v1/',
prepareHeaders: async (headers)=> {
const { data } = await dispatch(generalApi.endpoints.getWeatherKitToken.initiate(undefined, { forceRefetch: true }));
if (data?.token) {
headers.set('Authorization', `Bearer ${data.token}`);
}
return headers;
}
})(args, { signal, dispatch, getState }, extraOptions, ...additionalArgs),
Note, this is more of concept since i belive you don't want it that way.
To do that properly, you need to introduce an slice where you gonna keep your token, like that:
const slice = createSlice({
name: 'auth',
initialState: { user: null, token: null } as AuthState,
reducers: {},
// extraReducer
},
})
Next thing to do is to dispatch token in there on some query:
...
extraReducers: (builder) => {
builder.addMatcher(
api.endpoints.login.matchFulfilled,
(state, { payload }) => {
state.token = payload.token
state.user = payload.user
}
)
...
next you need to update your prepeareHeaders
function like so:
prepareHeaders: (headers, { getState }) => {
const token = (getState() as RootState).auth.token
if (token) {
headers.set('authorization', `Bearer ${token}`)
}
return headers
}
Lemme know if more explanations needed. Working example Working example