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 } |