Issue
I need to download query results from an endpoint by streaming results to a CSV file. This is in an effort to support enormous ResultSets being sent through the browser at one time.
Is there a way to accomplish this using Axios in the context of a React App?
I have seen fetch() and know that it has the following characteristics:
- returns ReadableStream
- Is NOT supported by IE11
- Does NOT allow for intercepting requests
- The status of a response relates to the request itself, not the HTTP status
- This means that the only way to receive an error would be to have something go wrong with the stream ending prematurely
- This definitely won't work for me since I have custom error-handling related to user permissions
Besides the ReadableStream
response type, the rest of the characteristics listed are not permissible. I will need to support IE11 and allow for intercepting requests / reading the HTTP status to determine how to handle the traffic.
Example with fetch
:
// The promise returned by `fetch` rejects if the fetch was unable to make HTTP-request
// e.g. network problems, or there’s no such site.
// Abnormal HTTP-statuses, such as 404 or 500 do not cause an error.
const results = await fetch(`${URL}/data`, {
method: 'post', // HTTP POST to send query to server
headers: {
Accept: 'application/json, text/plain, */*', // indicates which files we are able to understand
'Content-Type': 'application/json', // indicates what the server actually sent
},
body: JSON.stringify(query), // server is expecting JSON
credentials: 'include', // sends the JSESSIONID cookie with the address
}).then(res => res.json()) // turn the ReadableStream response back into JSON
.then((res) => {
if (res.ok) {
// boolean, true if the HTTP status code is 200-299.
console.log('response.ok!');
} else if (res.status === 401) {
throw Error(`You are not authenticated. Please login.`);
} else if (res.status === 403) {
throw Error(`You are not authorized to access this data.`);
} else {
throw Error(`Request rejected with status ${res.status}`);
}
})
.catch((error) => {
// catches error case and if fetch itself rejects
error.response = {
status: 0,
statusText:
'Cannot connect. Please make sure you are connected to internet.',
};
throw error;
});
console.log(results);
Example with axios
(not streaming)
Axios instance
import ...
const Api = axios.create({
baseURL: `${URL}`,
withCredentials: true,
});
// attach interceptors to requests and responses
// these are defined elsewhere and imported
Api.interceptors.request.use((request) => requestHandler(request));
Api.interceptors.response.use((response) => successHandler(response), (error) => errorHandler(error));
export default Api;
Axios request
const query = {"selections":{"TABLE_A":["COLUMN1"]},"filters":[{"predicates":[]}],"joins":[],"sorts":[],"limit":100,"offset":0}
const response = await Api.post('/data', query);
// further transformations to response to get formatted csv results required
Questions about Axios
- Is it possible to have a
ReadableStream
in Axios same asfetch
? - Is streaming in Axios only possible when assuming that it will be supported by Node in a server-side only setting?
- Sites like this appear to say that using
responseType: 'stream'
isn't something that can be done in the browser, only with Node.js usingfs
- Sites like this appear to say that using
- Is it possible to use
fetch
or something else in conjunction with Axios?
Issue
I need to download query results from an endpoint by streaming results to a CSV file. This is in an effort to support enormous ResultSets being sent through the browser at one time.
Is there a way to accomplish this using Axios in the context of a React App?
I have seen fetch() and know that it has the following characteristics:
- returns ReadableStream
- Is NOT supported by IE11
- Does NOT allow for intercepting requests
- The status of a response relates to the request itself, not the HTTP status
- This means that the only way to receive an error would be to have something go wrong with the stream ending prematurely
- This definitely won't work for me since I have custom error-handling related to user permissions
Besides the ReadableStream
response type, the rest of the characteristics listed are not permissible. I will need to support IE11 and allow for intercepting requests / reading the HTTP status to determine how to handle the traffic.
Example with fetch
:
// The promise returned by `fetch` rejects if the fetch was unable to make HTTP-request
// e.g. network problems, or there’s no such site.
// Abnormal HTTP-statuses, such as 404 or 500 do not cause an error.
const results = await fetch(`${URL}/data`, {
method: 'post', // HTTP POST to send query to server
headers: {
Accept: 'application/json, text/plain, */*', // indicates which files we are able to understand
'Content-Type': 'application/json', // indicates what the server actually sent
},
body: JSON.stringify(query), // server is expecting JSON
credentials: 'include', // sends the JSESSIONID cookie with the address
}).then(res => res.json()) // turn the ReadableStream response back into JSON
.then((res) => {
if (res.ok) {
// boolean, true if the HTTP status code is 200-299.
console.log('response.ok!');
} else if (res.status === 401) {
throw Error(`You are not authenticated. Please login.`);
} else if (res.status === 403) {
throw Error(`You are not authorized to access this data.`);
} else {
throw Error(`Request rejected with status ${res.status}`);
}
})
.catch((error) => {
// catches error case and if fetch itself rejects
error.response = {
status: 0,
statusText:
'Cannot connect. Please make sure you are connected to internet.',
};
throw error;
});
console.log(results);
Example with axios
(not streaming)
Axios instance
import ...
const Api = axios.create({
baseURL: `${URL}`,
withCredentials: true,
});
// attach interceptors to requests and responses
// these are defined elsewhere and imported
Api.interceptors.request.use((request) => requestHandler(request));
Api.interceptors.response.use((response) => successHandler(response), (error) => errorHandler(error));
export default Api;
Axios request
const query = {"selections":{"TABLE_A":["COLUMN1"]},"filters":[{"predicates":[]}],"joins":[],"sorts":[],"limit":100,"offset":0}
const response = await Api.post('/data', query);
// further transformations to response to get formatted csv results required
Questions about Axios
- Is it possible to have a
ReadableStream
in Axios same asfetch
? - Is streaming in Axios only possible when assuming that it will be supported by Node in a server-side only setting?
- Sites like this appear to say that using
responseType: 'stream'
isn't something that can be done in the browser, only with Node.js usingfs
- Sites like this appear to say that using
- Is it possible to use
fetch
or something else in conjunction with Axios?
2 Answers
Reset to default 15Streaming a response from the browser is not currently supported :
https://github.com/axios/axios/issues/479
Since we're dealing with XMLHttpRequests
in the browser, Axios is limited to the specification set by whatwg
. :
- https://xhr.spec.whatwg.org/#interface-xmlhttprequest
- https://github.com/whatwg/xhr
Specifically, these are the only supported types :
enum XMLHttpRequestResponseType {
"",
"arraybuffer",
"blob",
"document",
"json",
"text"
};
stream
is accepted when setting a responseType
in axios, but this is misleading. The adapter is going to be xhr.js
implicitly since we are using the browser which relies on XMLHttpRequests. HttpRequests are made on the server-side and will allow axios to use the http.js
adapter. THEN you can use stream
as a ResponseType with Node.js.
Using the fetch
API seems to be the only solution with a ReadableStream
as a response body type.
If you just need to download a file, using blob
in the responseType
options is definitely okay.
axios.post(url, param,
{ header: {...}, responseType: 'blob' }
)
.then(res => {
const link = document.createElement('a');
link.href = URL.createObjectURL(res);
link.click();
})