This is a short example on how to leverage nextJS API route to get and set cookies from an external server.
It gets the job done while not being the most efficient approach.
import axios, { CreateAxiosDefaults } from 'axios' | |
import { refreshToken as refreshTokenAdapter} from './session' | |
export const getJSONContentHeaders = () => { | |
"Content-Type": "application/json", | |
Accept: "application/json" | |
} | |
const axiosConfig: CreateAxiosDefaults = { | |
baseURL = '/url-to-backend' | |
headers: getJSONContentHeaders() | |
} | |
export default axios.create(axiosConfig) | |
export const axiosPrivate = axios.create({ | |
...axiosConfig, | |
withCredentials: true, | |
}) | |
export const getAuthHeader = (token: string) => ({ | |
"Authorization": `Bearer ${token}` | |
}) | |
export const setupAxiosInterceptors = (accessToken?: string, refreshToken: () => Promise<string> = refreshTokenAdapter) => { | |
const requestIntercept = axiosPrivate.interceptors.request.use( | |
async (config) => { | |
if (!config.headers["Authorization"]) { | |
let token = accessToken ?? await refreshToken(); | |
config.headers["Authorization"] = getAuthHeader(token)['Authorization']; | |
} | |
return config; | |
}, | |
(error) => Promise.reject(error) | |
); | |
const responseIntercept = axiosPrivate.interceptors.response.use( | |
(response) => response, | |
async (error) => { | |
const prevRequest = error?.config; | |
if (error?.response?.status === 403 && !prevRequest?.sent) { | |
prevRequest.sent = true; | |
const newAccessToken = await refreshToken(); | |
prevRequest.headers["Authorization"] = getAuthHeader(newAccessToken)['Authorization']; | |
return axiosPrivate(prevRequest); | |
} | |
return Promise.reject(error); | |
} | |
); | |
return { | |
axios: axiosPrivate, | |
cleanupInterceptors: () => { | |
axiosPrivate.interceptors.request.eject(requestIntercept); | |
axiosPrivate.interceptors.response.eject(responseIntercept); | |
} | |
}; | |
} |
import axios from "axios"; | |
const getErrorMessage = (err: unknown) => { | |
let message = "Something went wrong"; | |
if (axios.isAxiosError(err)) { | |
message = err.response?.data.message; | |
} else if (err instanceof Error) { | |
return (message = err.message); | |
} else { | |
message = String(err); | |
} | |
return message; | |
}; | |
export { getErrorMessage }; |
//api route for handling login /api/login | |
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction | |
import type { NextApiRequest, NextApiResponse } from "next"; | |
import { axiosPrivate } from "./axios"; | |
import { isAxiosError } from "axios"; | |
import { getErrorMessage } from "./getErrorMessage"; | |
export default async function handler( | |
req: NextApiRequest, | |
res: NextApiResponse | |
) { | |
//get whatever payload you are sending from the client | |
const formBody = req.formbody | |
try { | |
const response = await axiosPrivate.post( | |
"/sign-up", | |
formbody | |
); | |
if (response.status !== 200) throw new Error("Invalid response"); | |
//if there is a cookie | |
//if there is none, remove this line | |
const cookies = response.headers?.["set-cookie"]; | |
if (!cookies) { | |
throw Error("An error occurred try again later"); | |
} | |
//you can analyze pick out the refresh from the response and set it to a cookie, there probabaly is a way of fine-tuning it but you get the drift. | |
res.setHeader("Set-Cookie", jwtCookie); | |
res.status(200).json(response.data); | |
} catch (err) { | |
const responseStatus = isAxiosError(err) && err.response ? err.response.status : 500; | |
const message = getErrorMessage(err); | |
console.error({ | |
message, | |
responseStatus | |
}) | |
res.status(responseStatus).json(message); | |
} | |
} |
import axios from "./axios"; | |
import { getErrorMessage } from "./getErrorMessage"; | |
import { isAxiosError } from "axios"; | |
import { NextApiRequest, NextApiResponse } from "next"; | |
export default async function handler( | |
req: NextApiRequest, | |
res: NextApiResponse | |
) { | |
try { | |
const response = await axios.get("/refresh", { | |
headers: { | |
cookie: req?.headers?.cookie as string, | |
}, | |
}); | |
if (response.status !== 200) throw new Error("Invalid response"); | |
res.status(200).json(response.data); | |
} catch (err) { | |
const responseStatus = isAxiosError(err) && err.response ? err.response.status : 500; | |
const message = getErrorMessage(err); | |
console.error({ | |
message, | |
responseStatus | |
}) | |
res.status(responseStatus).json(message); | |
} | |
} |
const REFRESH_ENDPOINT = '/refresh/endpoint' | |
//alternatively to fetch, just use the axios instance with axios.get | |
const refreshToken = async () => { | |
const res = await fetch(REFRESH_ENDPOINT, { | |
headers: { | |
"Content-Type": "application/json", | |
Accept: "application/json" | |
} | |
}) | |
if(!res.ok) throw new Error('unauthorized) | |
const data = await res.json() | |
return data.token | |
} | |
export { refreshToken } |