SWR Nextjs : Auto Rotate Refresh Token
What stack that I use?
"swr": "^2.2.5",
"next": "14.2.3",
Problem
SPA (Single Page Application) are hard to identify user's session using JWT. In my case, I use JWT as token session that used for every interaction API.
Solution
We can use onErrorRetry
property in swr documentation. Here is my code to automatically refresh token when API return 401
and token_expired
code.
"use client";
import { SWRConfig } from "swr";
import { FetcherErrorException, fetcherSwrGet } from "./fetcher";
export function SwrConfig({ children }: { children: React.ReactNode }) {
return (
<SWRConfig
value={{
errorRetryCount: 3,
async onErrorRetry(err: FetcherErrorException, key, config, revalidate, { retryCount }) {
if (retryCount >= 3) return;
if (err.status === 404) return;
// My server return spesific code to identify expired token
// Verify here
if (err.response?.code === "token_expired") {
try {
// add your function to refresh token
await refreshtoken();
} catch (e: any) {
console.error(e)
if (e.status === 401) {
// if error 401, do force logout and redirect to login
logout()
window.location.href = "/auth/login"
return
}
}
// revalidate the request, ask swr to re-fetch the API
revalidate({ retryCount: retryCount + 1 })
}
}
}}
>
{children}
</SWRConfig>
);
}
You must return status
in error class and response
json also, here is my fetcher API looks like
export const fetchApi = async (...params: Parameters<typeof fetch>) => {
const token = useUserAuth.getState().token;
params[0] = new URL(params[0] as string, process.env.NEXT_PUBLIC_APP_SERVER);
const header = new Headers();
header.set('Content-Type', 'application/json');
params[1] = Object.assign(params[1] || {}, {
headers: header
})
if (token?.accessToken) {
header.set('Authorization', `Bearer ${token.accessToken}`)
}
const resFetch = await fetch(...params);
const resFetchJson = await resFetch.json();
// --------- Focus in this part ---------
if (!resFetch.ok) {
const message = resFetchJson?.message || resFetch.statusText;
throw new FetcherErrorException(message, {
response: resFetchJson,
status: resFetch.status,
})
}
return resFetchJson;
}
export class FetcherErrorException extends Error {
status: number;
response: any
constructor(message: string, options: {
status: number
response: any
}) {
super(message)
this.status = options.status
this.response = options.response
}
}
Subscribe to my newsletter
Read articles from Rio Chandra directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Rio Chandra
Rio Chandra
I am a Software Developer, solves complex problems while creating some of the most advanced applications in the industry. Provides mentorship and supervise developers. Has experience with Reactjs, VueJs, NestJs and other Framework.