Using HttpService to export as CSV: Unexpected token N in JSON - node.js

Currently, I am trying to export csv using nodejs as backend and angular as front end.
I know that the error 'Unexpected token N in JSON' means that there is a JSON parsing error. Which means that I need to change the {responseType: "blob" as "json"}. But there is a problem because I cannot include these in the parameters of httpServices because it only accepts HttpParams as parameters
So in respect to that I have tried these
1) return this.httpService.get(`/chatbots/get/${id}/download`, {responseType: 'text'}) which returns an error since httpService only accepts HttpParams as parameters
2) Change HttpServices from #core/services to HttpClient but it didnt work since I have another API call inside the current API call which uses HTTPServices.
3) Change it to a post method where I can attach {responseType: "blob" as "json"} but it didnt work and anyway it should work as a get?
Currently, output already shows the text in csv.
router.get('/get/:id/download', passport.authenticate('jwt', {
session: false
}), (req, res, next) => {
.....
console.log(output)
res.attachment('test.csv')
res.send(output)
}
In services:
import { HttpService } from '../../#core/services';
constructor(
private httpService: HttpService, ) {}
...
downloadCSV(id: string) {
return this.httpService.get(`/xxxx/get/${id}/download`)
}
in component
export() {
if (this.id) {
this.chatbotService.downloadChatbot(this.id)
.subscribe((data: any) => {
const blob = new Blob([data], { type: 'text/csv' });
const fileName = `${this.id}-test.csv`;
saveAs(blob, fileName);
})
}
}
Even though the status is 200, it says
SyntaxError: Unexpected token N in JSON at position 0
at JSON.parse ()
at XMLHttpRequest.onLoad (http://localhost:8080/vendor.js:22263:51)
at ....

By default, a request made with HttpClient is considered to have a JSON response.
To get a CSV, you can force a text response with
downloadCSV(id: string) {
return this.httpService.get(`/xxxx/get/${id}/download`, {responseType: 'text'});
}
You can also read this part of the documentation : https://angular.io/api/common/http/HttpRequest#responseType

Related

There is a way to make Axios return the data as default response?

When we use Axios we always have to get the data from response. Like this:
const response = await Axios.get('/url')
const data = response.data
There is a way to make Axios return the data already? Like this:
const data = await Axios.get('/url')
We never used anything besides the data from the response.
You can use ES6 Destructing like this:
const { data } = await Axios.get('/url');
So you won't have write another line of code.
add a response interceptors
axios.interceptors.response.use(function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response.data; // do like this
}, function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
});
what i normally do is create a js file called interceptors.js
import axios from 'axios';
export function registerInterceptors() {
axios.interceptors.response.use(
function (response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response.data;
},
function (error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
}
);
}
in ./src/index.js
import { registerInterceptors } from './path/to/interceptors';
registerInterceptors();//this will register the interceptors.
For a best practice don't use axios every where, just in case in the future if you want to migrate to a different http provider then you have to change everywhere it uses.
create a wrapper around axios and use that wrapper in your app
for ex:
create a js file called http.js
const execute = ({url, method, params, data}) => {
return axios({
url,
method,//GET or POST
data,
params,
});
}
const get = (url, params) => {
return execute({
url, method: 'GET', params
})
}
const post = (url, data) => {
return execute({
url, method: 'POST', data
})
}
export default {
get,
post,
};
and use it like
import http from './http';
....
http.get('url', {a:1, b:2})
so now you can customize all over the app, even changing the http provider is so simple.

Uploading JSON Object using s3.putObject uploads key but not the value

I am crafting a simple json object and uploading it to digital ocean using the s3.putObject function. There are no problems getting it to upload but when I look at it on digital ocean, only the key is there in the json object, and the value shows {}
Here is the code creating the JSON, and uploading it:
async function sendErrorData(error){
var errorfile = {
'errorLog' : error
}
console.log(errorfile)
const params = {
Body: JSON.stringify(errorfile),
Bucket: 'MyBucket'
Key: 'errors.json',
ContentType: "application/json"
};
await uploadToDO(params)
.then((data) => console.log(JSON.stringify(data)))
.catch((err) => console.log(JSON.stringify(err)))
console.log(errorfile)
}
function uploadToDO(params) {
return s3.putObject(params).promise()
}
The console logs before and after the upload show the object perfectly fine, but once uploaded it's missing the values like this.
{
"errorLog": ReferenceError: ....
}
Uploaded:
{
"errorLog": {}
}
{
"errorLog": ReferenceError: ....
}
Is invalid JSON by the looks of things. You ask AWS to upload as application/json file and it is not so fails.
Therefore when you construct the errorfile
var errorfile = {
'errorLog' : JSON.stringify(error)
}
Note: This will save the error possibly as a string and not as a JSON object. If you need it as a JSON object you'd need to construct it yourself.
You are awaiting on the function call uploadToDO(params). But the function uploadToDO is not defined as an async function.
it should be:
async function uploadToDO(params) {
return s3.putObject(params).promise()
}
Hope this helps.

Using Axios to return stream from Express app -- "The provided value 'stream' is not a valid enum value of type XMLHttpRequestResponseType."

I'm learning Node, Express and React and am trying to convert a simple generic Fetch method that I use to call my Express API to Axios. I've gotten Axios to work when I want it to automatically return the result as JSON data, but here I want it to return a ReadableStream. From the documentation it should be simple: simply add the field responseType:'stream' to the config but I keep getting the error The provided value 'stream' is not a valid enum value of type XMLHttpRequestResponseType. I see that others have had this issue (see https://github.com/axios/axios/issues/1474)but I don't see any solution. Does anyone know if there is one, or if I am doing something wrong? The relevant code is below. Thanks for any insights!
const logic = {
_call(path, method, headers, body, expectedStatus) {
const config = { method,responseType:'stream', url: `http://localhost:8080/${path}`}
if (headers) config.headers = headers
if (body) config.data = body
return axios( config)
.then(res => {
if (res.status === expectedStatus) {
return res
} else
return res
.then(({ message }) => {
throw new Error(message)
})
})
}
}

NodeJs handling json between http and fetchjsonp

My application using fetch-jsonp is failing to read JSON from a site I serve from a simple http module. I get an error: "Uncaught SyntaxError: Unexpected token :" and then a time-out error.
I'm trying out ReactJS and so put together this react component
import React, { Component } from 'react';
import fetchJsonp from 'fetch-jsonp';
var data = { 'remote':{}, 'local':{} };
var fetched= false;
class FastTable extends Component {
loadData(url,element) {
return fetchJsonp(url)
.then(function(response) {
return response.json(); })
.then((responseJson) => {
data[element] = responseJson;
this.setState(data);
return responseJson;
})
.catch((error) => {
console.error(error);
});
}
render() {
if (!fetched) {
this.loadData('https://jsonplaceholder.typicode.com/posts/1','remote');
this.loadData('http://localhost','local');
}
fetched=true;
return (
<div><pre>{JSON.stringify(data, null, 2) }</pre></div>
);
}
}
export default FastTable;
It uses the fetchJsonp to grab a JSON dataset from a test website, which works - and then from my http test site, which doesn't.
The test server has the following code:
var http = require('http');
http.createServer(function (req, res) {
var result = {
'Bob':'Likes cheese'
};
res.writeHead(200, {'Content-Type': 'application/json'});
res.write(JSON.stringify(result));
res.end();
}).listen(80);
I've also had mixed results reading from other JSON servers within our project.
Why is fetch-jsonp not reading from my test site? Should I be reading this data in another way?
More than likely, you are calling a JSON API, which does not support
JSONP. The difference is that JSON API responds with an object like
{"data": 123} and will throw the error above when being executed as a
function. On the other hand, JSONP will respond with a function
wrapped object like jsonp_123132({data: 123}).
If you want to work with JSON API try axios or supergent npm.

Render raw image bytes to response body

I'm creating an API that creates authorized API calls to Google's APIs, specifically Drive for this question. My API is working fine and uses Google's Node API to make the requests. When I fire off a request to this resource, I get back the following response:
{
"kind": "drive#file",
"id": "...",
"name": "bookmobile.jpg",
"mimeType": "image/jpeg"
}
I use the above response to determine the MIME type of the file I'm to display later. I then make a subsequent call to the same endpoint, but specifying alt=media as an option to download the file as specified in Google's Guide. If I console.log or res.send() the response, I get the following output:
Which we can see is the raw image bytes from the API call. How do I render these bytes to the response body properly? My code is as follows:
// DriveController.show
exports.show = async ({ query, params }, res) => {
if (query.alt && query.alt.toLowerCase().trim() === 'media') {
// Set to JSON as we need to get the content type of the resource
query.alt = 'json'
// Get the Files Resource object
const options = createOptions(query, params.fileId)
const filesResource = await Promise.fromCallback(cb => files.get(options, cb))
// Grab the raw image bytes
query.alt = 'media'
await createAPIRequest(createOptions(query, params.fileId), 'get', res, filesResource)
} else {
await createAPIRequest(createOptions(query, params.fileId), 'get', res)
}
}
async function createAPIRequest (options, method, res, filesResource = {}) {
try {
const response = await Promise.fromCallback(cb => files[method](options, cb))
if (filesResource.hasOwnProperty('mimeType')) {
// Render file resource to body here
} else {
res.json(response)
}
} catch (error) {
res.json(error)
}
}
Searching through various answers here all seem to point to the following:
res.type(filesResource.mimeType)
const image = Buffer.from(response, 'binary')
fs.createReadStream(image).pipe(res)
But this kills my Express app with the following error:
Error: Path must be a string without null bytes
How would I go about rendering those raw image bytes to the response body properly?
The Google API client returns binary data as a string by default, which will corrupt image data when it happens. (The issue is discussed on this thread: https://github.com/google/google-api-nodejs-client/issues/618). To fix, use the encoding: null option when requesting the file contents:
files[method](options, { encoding: null }, cb))

Resources