I want to add refresh token functionality at server side in nextjs. From the server side I means that the api that I call at server in page.js file. I am not using nextjs inbuilt backend.
Basically I am using nextjs with custom server (reference link) with nodejs and expressjs.
Below is my code with I call a server side api and server axios interceptor for refresh logic.
The main problem that I am facing is I cannot set cookies.
The error is –
Error: Cookies can only be modified in a Server Action or Route Handler.
page.js file
const fetchMembersData = async () => {
let returnData = {status: false, data: [], statusCode: null};
try {
const { data } = await serverAxios.post(MEMBERS.LIST, {
page: 1, limit: 12, sortOrder:"ASC", sortBy:"name"
});
if(data.status == API_STATUS.SUCCESS) {
returnData = data;
returnData.statusCode = API_STATUS.SUCCESS;
}
} catch (e) {
console.log(`error `, e);
returnData.statusCode = e?.status || null;
}
finally {
return returnData;
}
}
const page = async () => {
const data = await fetchMembersData();
if(!data.status) {
handleServerAction(data.statusCode);
return;
}
return (
<Main data={data} />
)
}
export default page
serverAxios.js file
import { API_STATUS, BASE_URL, COOKIE_DEFAULT_EXPIRE } from "../constant";
import momentTimezone from 'moment-timezone';
import { AUTH } from "../apiConstant";
import axios from "axios";
import createAuthRefreshInterceptor from "axios-auth-refresh";
import { cookies } from "next/headers";
// Create an Axios instance
const AxiosInstance = axios.create({ baseURL: BASE_URL });
// Intercept request
AxiosInstance.interceptors.request.use(async (request) => {
const cookieStore = await cookies();
const token = cookieStore.get("token")?.value || "";
if (token) {
request.headers.Authorization = `Bearer ${token}`;
}
request.headers.tz = momentTimezone.tz.guess();
return request;
}, (error) => {
return Promise.reject(error);
});
// Refresh token function
const refreshTokenFunction = async (failedRequest) =>
axios.post(`${BASE_URL}/${AUTH.REFRESH_TOKEN}`, {}, {
headers: {
// "refresh-token": Cookies.get("the-50-group-refresh-token") || ""
"refresh-token": await cookieStore.get("refresh-token")?.value || ""
}
}).then(resp => {
console.log(`respo data --> `, resp.data); //DEBUGGING
if (resp.data.status == API_STATUS.SUCCESS) {
const { access_token: token, refresh_token } = resp.data;
// Set the new token globally
AxiosInstance.defaults.headers.Authorization = `Bearer ${token}`;
/**
* Set access tokena and refresh token in cookies
* But got error that cookies can only be set in server action or route handler
*/
// Update the failed request's Authorization header
failedRequest.response.config.headers.Authorization = `Bearer ${token}`;
return Promise.resolve();
}
else {
cookieStore.delete('token');
cookieStore.delete('refresh-token');
throw new UnauthorizedError('Unauthorized');
}
}).catch((error) => {
console.log(`refreshTokenFunction error --> `, error); //DEBUGGING
throw new UnauthorizedError('Unauthorized');
});
// Create axios interceptor
createAuthRefreshInterceptor.default(AxiosInstance, refreshTokenFunction);
// Intercept response
AxiosInstance.interceptors.response.use((response) => {
return Promise.resolve(response);
}, async (error) => {
return Promise.reject(error);
});
export default AxiosInstance;
