12

I'm trying to build an interceptor for cases when the access token becomes invalid with RTK Query. I've built it by an example in the docs, so that is looks as follows:

const baseQuery = fetchBaseQuery({
    baseUrl: BASE_URL,
    prepareHeaders: (headers, { getState }) => {
        const {
            auth: {
                user: { accessToken },
            },
        } = getState() as RootState;
        if (accessToken) {
            headers.set('authorization', `Bearer ${accessToken}`);
        }
        return headers;
    },
});

const baseQueryWithReauth: BaseQueryFn<string | FetchArgs, unknown, FetchBaseQueryError> = async (
    args,
    api,
    extraOptions
) => {
    let result = await baseQuery(args, api, extraOptions);

    if (result.error && result.error.status === 401) {
        const refreshResult = await baseQuery('token/refresh/', api, extraOptions);

        if (refreshResult.data) {
            api.dispatch(tokenUpdated({ accessToken: refreshResult.data as string }));

            // retry the initial query
            result = await baseQuery(args, api, extraOptions);
        } else {
            api.dispatch(logout());
        }
    }
    return result;
};

export const baseApi = createApi({
    reducerPath: 'baseApi',
    baseQuery: baseQueryWithReauth,
    endpoints: () => ({}),
});

The problem is that the token/refresh/ expects a POST request with a refresh token its body and I can't figure out how to rebuilt this line const refreshResult = await baseQuery('token/refresh/', api, extraOptions); for it to accept parameters and make POST request.

5
  • It'd be also nice to know how to separate requests to private and public endpoints Commented Jul 29, 2021 at 19:04
  • 1
    I'm curious to know how do you make all the other api slices extend from this baseQueryWithReauth Commented Oct 24, 2021 at 4:38
  • 1
    @Jose Bernhardt Just like that export const yourApiName = baseApi.injectEndpoints({ endpoints: (builder) => ({}), }); Commented Oct 25, 2021 at 8:47
  • thanks, Do you know if there is a way to override more values ? (I cant seem to find it in their docs) like "injecting" a fetchBaseQuery as well? It would be pretty convenient to do this for cases where you want to inject endpoints with different baseUrl. Commented Oct 25, 2021 at 18:18
  • @JoseBernhardt I'm not sure I understand what you mean. But I reckon if you've got 2 separate base URLs it would make sense to have the endpoints for it in separate files. Something like someApi = baseApiMain.injectEndpoints and someOtherApi = baseApiSecondary.injectEndpoints Commented Oct 26, 2021 at 6:34

2 Answers 2

11

instead of baseQuery('token/refresh/', api, extraOptions); you can also do

baseQuery({
  url: 'token/refresh/',
  method: 'POST'
}, api, extraOptions);

The first argument to fetchBaseQuery is just what you would return from the query function in an endpoint definition.

As for your other question, I don't know what exactly you mean by "public" and "private" endpoints. It is your code who calls those queries, so you should know when to call which ones?

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for your reply. What I meant in my other question is how do I manually control whether or not I attach the authentication token? Let's say I have some endpoints that I need to call when I'm authenticated but the endpoints themselves are public (don't require auth token). From my understanding the token will be attached to each and every request if the prepareHeaders method finds it in the store
I would assume that sending the token just doesn't hurt in those cases. If this is an external service with completely unrelated data (so you don't want to send the token there for privacy reasons), you could also create a seconds api for that.
2

I needed to bypass adding the auth token to a refreshToken request recently. I did that by creating an endpoint that used queryFn rather than query:

tokenRefresh: builder.query<TokenRefreshResponse, void>({
    queryFn: async (arg, queryApi, extraOptions, baseQuery) => {
        const response = await fetch(`/api/refresh`);
        return (response.ok) ? {data: await response.json()}
                             : {error: await response.json()};
    }
}),

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.