Preface
When it come to align fetch in both frontend and backend environment, axios
is the most frequent libray which be mentioned. However, what is the diffenence between Client/Server side fetch?
Axios
We could use axios
to make a request in both Browser side or server side directly. This is because axios
itself handles and wraps the diffetent envireonments of the browser and Node, and then exposes them through a single interface. In the browser, axios uses XMLHttpRequest(XHR)
to handle requests, while in the Node environment, it uses the library http/https
. Below is a simplified demo based on the axios source code.
Handling in the browser — funcion
xhrAdapter
would return a Promise based onXMLHttpRequest
.export async function xhrAdapter(config) { return new Promise((resolve, reject) => { const request = new XMLHttpRequest(); request.open(config.method.toUpperCase(), config.url, true); Object.keys(config.headers).forEach((key) => { request.setRequestHeader(key, config.headers[key]); }); request.onload = function () { const response = { data: JSON.parse(request.responseText), status: request.status, statusText: request.getAllResponseHeaders(), config: config, request: request, }; resolve(response); }; request.onerror = function () { reject(new Error("Network Error")); }; request.ontimeout = function () { reject(new Error(`Timeout of ${config.timeout} ms exceeded`)); }; request.send(config.data); }); }
In the Node environment, is
httpAdpater
function, which return a Promise basedhttp
orhttps
.import http from "http"; import https from "https"; export async function httpAdpater(config) { return new Promise((resolve, reject) => { const url = new URL(config.url); const isHttps = url.protocol === "https"; const options = { method: config.method, headers: config.headers, }; const request = (isHttps ? https : http).request( url, options, (response) => { let data = ""; response.on("data", (chunk) => { data += chunk; }); response.on("end", () => { resolve({ data: JSON.parse(data), status: response.statusCode, statusText: response.statusMessage, headers: response.headers, config: config, request: response, }); }); }, ); request.on("error", (error) => { reject(error); }); if (config.data) { request.write(config.data); } request.end(); }); }
Finally, will check the environment is in the browser or in the Node , in order to return the right instance—if browser, return
xhrAdapter
; if Node environment, returnhttpAdpater
。async function getDefaultAdapter() { if (typeof XMLHttpRequest !== "undefined") { // for browser, using XHR adapter const { xhrAdapter } = await import("./xhrAdapter.js"); return xhrAdapter; } else if (typeof process !== "undefined") { // for Node env, using HTTP Adpater const { httpAdpater } = await import("./httpAdpater.js"); return httpAdpater; } throw new Error("No suitable adapter found"); }
The completed mocking axios source code could refer throught the link
fetch
Since 2015, there has been a new Fetch API
in Browser environment, and it has become the standard in web async request. However, there are still no comparable Fetch API in Node environment, so many SDKs choose node-fetch
library to make up for this.
Bascially, it is also related with checking global environment—if in Browser environment, could use native fetch directly; if in Node environment, could import node-fetch
dyamicly. (no dyamicly would cause compile error)
export async function initFetch() {
let fetch;
if (typeof window !== "undefined" && typeof window.fetch === "function") {
fetch = window.fetch;
} else {
const nodeFetch = await import("node-fetch");
fetch = nodeFetch.default;
}
if (typeof global !== "undefined") {
global.fetch = fetch;
} else if (typeof window !== "undefined") {
window.fetch = fetch;
}
}
However, the latest version of node-fetch
only support ESM, so it would cause compiling error in the commonjs project. In this sicuation, could replace node-fetch
with http/https
—
import http from "http";
import https from "https";
import { URL } from "url";
function fetch(url, options = {}) {
return new Promise((resolve, reject) => {
const urlObj = new URL(url);
const isHttps = urlObj.protocol === "https:";
const { method = "GET", headers = {}, body } = options;
const requestOptions = {
method,
headers,
};
const request = (isHttps ? https : http).request(
urlObj,
requestOptions,
(response) => {
let data = "";
response.on("data", (chunk) => {
data += chunk;
});
response.on("end", () => {
const responseData = {
ok:
response.statusCode >= 200 &&
response.statusCode < 300,
status: response.statusCode,
statusText: response.statusMessage,
headers: response.headers,
url: response.url || url,
json: () => Promise.resolve(JSON.parse(data)),
text: () => Promise.resolve(data),
blob: () => Promise.resolve(Buffer.from(data)),
};
resolve(responseData);
});
},
);
request.on("error", (error) => {
reject(error);
});
if (body) {
request.write(body);
}
request.end();
});
}
export default fetch;
Finally, make the global fetch pointo to the fetch function.
- could checkt the compeleted mock fetch source code through link
Conclusion
Thurdermore, many third pary libaraies and SDKs avoid using Axios
for network requests, primarily to reduce additional dependenies and minimize the bondle size. Additionally, since the native Fetch API in Node.js is only available starting from version 21, manual wrapping of the http/https
modules or directly using node-fetch
is still necessary for backward compatibility.
The completed source code is on github js-fetch_vs_axios
Ref
ChangeLog
- 20240609-init