const acceptRequest = async () => {
try {
const res = await fetch(`${process.env.NEXT_PUBLIC_SOCKET_SERVER_URL}/acceptRequest`, {
method: "POST",
body: JSON.stringify({ id: enemyid }),
credentials: "include",
headers: {
"Content-Type": "application/json",
},
});
if (res.status === 200) toast("Working");
if(res.status === 401)await refreshTokenFn()
} catch (err) {
if (err instanceof Error) toast(err.message);
setPending(false);
}
};
I have a refreshToken function that I can call to refresh the token. I want the fetch request to retry automatically after refreshing the token, without duplicating the entire code.
This is how I am handling it which is not good enough for me.
const acceptRequest = async () => {
try {
const res = await fetch(`${process.env.NEXT_PUBLIC_SOCKET_SERVER_URL}/acceptRequest`, {
method: "POST",
body: JSON.stringify({ id: enemyid }),
credentials: "include",
headers: {
"Content-Type": "application/json",
},
});
if (res.status === 200) toast("Working");
if(res.status === 401){
await refreshTokenFn()
const res = await fetch(`${process.env.NEXT_PUBLIC_SOCKET_SERVER_URL}/acceptRequest`, {
method: "POST",
body: JSON.stringify({ id: enemyid }),
credentials: "include",
headers: {
"Content-Type": "application/json",
},
});
if (res.status === 200) toast("Working");
}
} catch (err) {
if (err instanceof Error) toast(err.message);
setPending(false);
}
};
Create a wrapper for API requests that can do two things...
For example
api.js
import refreshTokenFn from './somewhere';
// hold any in-progress refresh requests in this promise
let refreshPromise;
const defaultOptions = { credentials: 'include' };
const api = async (url, options) => {
// it might be handy to construct full URLs here
const res = await fetch(
new URL(url, process.env.NEXT_PUBLIC_SOCKET_SERVER_URL),
{ ...defaultOptions, ...options },
);
if (res.ok) {
return;
}
if (res.status === 401) {
if (!refreshPromise) {
// note there's no `await`, we just set up the hold on the refresh process
refreshPromise = refreshTokenFn();
}
await refreshPromise; // wait for in-progress refresh requests
refreshPromise = null; // clear the promise once resolved
// recursively call the request with the same params
return api(url, options);
}
throw new Error(`${res.status} ${res.statusText}`);
};
export default api;
Then you can just use this function for all API requests without worrying about the refresh logic
import api from './api';
const acceptRequest = async () => {
try {
await api('/acceptRequest', {
method: 'POST',
body: JSON.stringify({ id: enemyId }),
headers: { 'Content-Type': 'application/json' },
});
toast('Working');
} catch (err) {
toast(err.message);
setPending(false);
}
};
Demo...
// Mocks
let authValid = false;
const fetch = async (url) => {
console.log('fetch:', url);
if (!authValid) {
console.warn('fetch: request unauthorized');
return { ok: false, status: 401 };
}
console.log('fetch: request ok');
return { ok: true, status: 200 };
};
const refreshTokenFn = () => {
console.log('refreshTokenFn: Refreshing token');
return new Promise((r) => {
setTimeout(() => {
authValid = true;
console.log('refreshTokenFn: Token refreshed');
r();
}, 1000);
});
};
const process = { env: { NEXT_PUBLIC_SOCKET_SERVER_URL: 'https://example.com/' } };
// Answer code
let refreshPromise;
const defaultOptions = { credentials: 'include' };
const api = async (url, options) => {
// it might be handy to construct full URLs here
const res = await fetch(
new URL(url, process.env.NEXT_PUBLIC_SOCKET_SERVER_URL),
{ ...defaultOptions, ...options },
);
if (res.ok) {
return;
}
if (res.status === 401) {
if (!refreshPromise) {
// note there's no `await`, we just set up the hold on the refresh process
refreshPromise = refreshTokenFn();
}
await refreshPromise; // wait for in-progress refresh requests
refreshPromise = null; // clear the promise once resolved
// recursively call the request with the same params
return api(url, options);
}
throw new Error(`${res.status} ${res.statusText}`);
};
// Test
['/acceptRequest', '/some-other-url', '/and-another'].forEach(async (url) => {
await api(url, {});
console.log('API request complete:', url);
});
.as-console-wrapper { max-height: 100% !important; }