Handling authentication token refreshing with fetch interceptor #3064
Replies: 2 comments 4 replies
-
|
Hi @jezzsantos! I'm Dosu and I’m helping the openapi-ts team. With fetch, request bodies (for PUT/POST) are streams and can only be read once, so you can't access the original body after the request fails unless you buffer it yourself. The openapi-ts fetch client exposes the original request in interceptors, but does not provide built-in retry or body preservation mechanisms docs. To reliably retry requests after a 401, you need to preserve the request body before sending. The most common pattern is to buffer the body in memory (for example, as a string, Blob, or ArrayBuffer) and use that buffer for both the initial request and any retries. If you use JSON, keep the original object or string and pass it as the body each time. Here’s a sketch of how you might do this: client.interceptors.response.use(async (response, request) => {
if (response.status === 401) {
await refreshToken();
// If you buffered the body before, you can reuse it here:
const retryRequest = new Request(request.url, {
...request,
body: request._bufferedBody // your custom property
});
return fetch(retryRequest);
}
return response;
});
client.interceptors.request.use((request) => {
// Buffer the body before sending
if (request.body && typeof request.body === 'string') {
request._bufferedBody = request.body;
}
// For JSON, you could store the object or string
return request;
});If you use streams (like FormData or file uploads), you’ll need to buffer them as well, which can be tricky for large payloads. For most APIs, keeping the body as a string or object is enough. This approach lets you retry the original request after refreshing the token, but be careful with large bodies and non-repeatable streams. For more details on interceptors and client config, see the docs. To reply, just mention @dosu. How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other |
Beta Was this translation helpful? Give feedback.
-
|
Thanks again @dosu Any reason why I couldn't/shouldn't do something like this: Request Interceptor: function cacheRequestBody(request: Request, _options: any) {
const clonedRequest = request.clone();
if (clonedRequest) {
// @ts-ignore
request._bufferedRequest = clonedRequest;
}
return request;
}Response Interceptor: async function handleUnauthorizedResponse(response: Response, request: Request): Promise<Response> {
// Only handle 401s
if (response.status !== 401) {
return Promise.resolve(response);
}
return await refreshToken().then(async (res) => {
if (res.response.ok) {
const retry = await fetch(request._bufferedRequest);
if (retry.ok) {
return Promise.resolve(retry);
} else {
if (retry.status === 401) {
logout().then(() => forceReloadHome());
return Promise.resolve(response); // should never get here, this should bypass this error altogether
} else {
return Promise.resolve(retry); // This response will be different from the one passed into this interceptor.
}
}
} else {
if (res.response.status === 423 || res.response.status === 401) {
logout().then(() => forceReloadHome());
return Promise.resolve(response); // should never get here, this should bypass this error altogether
} else {
});
return Promise.resolve(res.response); // This response will be different from the one passed into this interceptor.
}
}
});
} |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
I am trying to handle when any 401 is returned from any client request.
I am handling it in a response interceptor. I am only using fetch, not axios or any other client.
When this happens, I am calling my generated
refreshToken()method, and if that succeeds I am attempting to retry the original request that failed with the 401.My initial thought was to use use a new instance of fetch to make the original request to bypass the generated client.
To do that for a GET request is easy, but for a PUT, POST request, I need access to the original request's body, which has already been read!
If I continue to use fetch, how can I access the original request body, so that I can assemble the original request again, when it already failed?
(or is there another supported way of retrying the original request)?
Beta Was this translation helpful? Give feedback.
All reactions