Fetch adapter Post request do not working in serverside
Describe the bug
I use latest axios write an interceptor for both client and server in my NextJS project. When use it with default fetch it working but when i try it with new axios fetch adapter it return to my server with no body in it (params still work and method still POST).
To Reproduce
Install nextJS version ^14 (using App Router) Install axios 1.7.2 in the nextJS middleware.ts file call an api by using axios:
export async function middleware(request: NextRequest) {
// Check if the request is for the sign-in page
let isAuthenticated = false;
const response = intlMiddleware(request);
try {
const user = await getMyProfile();
if (user) {
isAuthenticated = true;
response.cookies.set('user', JSON.stringify(user), { httpOnly: false });
}
} catch (e) {
if (isAxiosError(e)) {
console.log('Error:', e.message);
}
}
if (includes(NOT_AUTH_APP_ROUTES, removeLocale(request.nextUrl.pathname))) {
if (isAuthenticated) {
return NextResponse.redirect(new URL('/', request.url));
}
}
// Apply internationalization middleware
return response;
}
write an interceptor like below
Code snippet
import axios, { AxiosError } from 'axios';
import Cookies from 'js-cookie';
import API from '@/constant/API';
import { BE_URL } from '@/constant/common';
import { isServer } from '@/utils/common';
import { parseCookies } from '@/utils/cookie';
const axiosClient = axios.create({
baseURL: BE_URL,
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
withCredentials: true,
adapter: isServer() ? 'fetch' : undefined,
});
let isRefreshing = false;
type FailedQueue = {
resolve: (value: unknown) => void;
reject: (reason?: any) => void;
};
let failedQueue: FailedQueue[] = [];
function processQueue(error: AxiosError | Error | null) {
failedQueue.forEach((prom) => {
if (error) prom.reject(error);
else prom.resolve(null);
});
failedQueue = [];
}
axiosClient.interceptors.response.use(
(response) => {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
},
async (err) => {
let refresh_token: string | undefined = undefined;
if (err.config.adapter === 'fetch') {
const cookies = parseCookies(err.config.headers.Cookie);
refresh_token = cookies.refresh_token;
} else {
refresh_token = Cookies.get('refresh_token');
}
if (!refresh_token) {
return Promise.reject(err);
}
const originalConfig = err.config;
if (originalConfig.url !== '/' && err.response) {
if (isRefreshing) {
return new Promise((resolve, reject) => {
failedQueue.push({ resolve, reject });
}).catch((err) => Promise.reject(err));
}
isRefreshing = true;
if (err.response.status === 401 || err.response.status == 403)
return new Promise((resolve, reject) => {
const f = async () => {
try {
const formData = new FormData();
formData.append('refresh_token', refresh_token);
await axios.post(API.AUTH.REFRESH_TOKEN, formData, {
baseURL: BE_URL,
adapter: 'fetch',
withCredentials: true,
});
// Working with default fetch
// const res = await fetch(`${BE_URL}${API.AUTH.REFRESH_TOKEN}`, {
// method: 'POST',
// body: formData,
// credentials: 'include',
// });
processQueue(null);
resolve(axiosClient(originalConfig));
} catch (err) {
if (axios.isAxiosError(err) || err instanceof Error) {
processQueue(err);
}
if (!isServer()) window.location.href = '/';
reject(err);
} finally {
isRefreshing = false;
}
};
f();
});
}
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(err);
}
);
export default axiosClient;
Expected behavior
Axios with fetch request still send an POST request with body in it
Axios Version
1.7.2
Adapter Version
FETCH
Browser
Firefox
Browser Version
127.0
Node.js Version
18.17.0
OS
Mac OS
Additional Library Versions
Next 14.2.2
React 18
## Server
Django 5.0.4
Python 3.11
Additional context/Screenshots
Testing with Default WebAPI fetch()
To make your code looks easier, use https://github.com/Flyrell/axios-auth-refresh this plugin to refresh token.
Have you managed to resolve this issue? 👀
Okay, seems like I found the problem. Apparently adapter Fetch was sending the requests with 'Transfer-Encoding': 'chunked' header and 'Content-Length': ''.
So I created Axios interceptor:
axiosInstance.interceptors.request.use((config) => {
if (config.data && typeof config.data === "object") {
const jsonData = JSON.stringify(config.data);
config.data = jsonData;
config.headers["Content-Length"] = Buffer.byteLength(jsonData).toString();
config.headers["Content-Type"] = "application/json";
}
return config;
});
Hope this helps. 🔥
Or try xiorjs, similar API, naturally born with fetch, turn your axios size load speed from 16ms to 4ms.