Getting XMLHttpRequest error from HTTP GET request on unformatted JSON - node.js

I'm trying to get JSON-data via HTTP from my Dart/Flutter function:
Future<List<News>?> getNews() async {
var client = http.Client();
var uri = Uri.parse('http://localhost:3000/news');
var response = await client.get(uri);
if (response.statusCode == 200) {
var jsonFile = response.body;
try {
return newsFromJson(jsonFile);
} catch (e) {
print(e);
}
}
return null;
}
The Json-File looks like this:
{
"news": [
{
"id": 0,
"title": "Test",
"text": "Test",
"buttonText": "Test",
"source": "Test",
"showButton": false,
"openFile": false,
"openWebsite": true
},
{
"id": 1,
"title": "Test",
"text": "Test",
"buttonText": "Test",
"source": "Test",
"showButton": false,
"openFile": false,
"openWebsite": true
}
]
}
When I start the following Script for the server that is going to provide the data, everything works fine but the json-data is NOT formatted when I call it in the browser:
const express = require('express');
const fs = require('fs');
const app = express();
app.get('/news', (req, res) => {
console.log('Received request');
fs.readFile('data.json', (err, data) => {
if (err) throw err;
const news = JSON.parse(data).news;
res.json(news);
});
});
app.listen(3000, () => {
console.log('Server listening on port 3000');
});
The request from my dart code reaches the NodeJS-Script but ends with the mentioned XMLHttpRequest error. And here comes the interesting thing: When I use the tool json-server (https://github.com/typicode/json-server) with the same json-file, everything IS formatted when calling the url in browser and my Flutter/Dart codes work without any error. So in conclusion: The NodeJS-Script is working like the json-server tool. The only difference is, that the json provided by the NodeJS script isn't formatted in the browser which might causes the error.
Where is the problem?
Could be useful
List<News> newsFromJson(String str) =>
List<News>.from(json.decode(str).map((x) => News.fromJson(x)));
Error: XMLHttpRequest error.
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 299:10 createErrorWithStack
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/core_patch.dart 341:28 _throw
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/core/errors.dart 116:5 throwWithStackTrace
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/zone.dart 1378:11 callback
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/schedule_microtask.dart 40:11 _microtaskLoop
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/async/schedule_microtask.dart 49:5 _startMicrotaskLoop
C:/b/s/w/ir/cache/builder/src/out/host_debug/dart-sdk/lib/_internal/js_dev_runtime/patch/async_patch.dart 166:15 <fn>

Express.js will try to be compact about the data it sends, rather than pretty.
Format it explicitly:
res.set('Content-Type', 'application/json')
res.send(JSON.stringify(news, undefined, 2));
If you want the JSON data to be formatted exactly as it is in the file, and send the whole file, not a specific part of the JSON, just don't parse it:
res.set('Content-Type', 'application/json');
res.send(data);

Related

How to modify html response using Vite proxy configuration

I have a following Vite configuration:
import { defineConfig } from "vite";
const zlib = require("zlib");
export default defineConfig(() => {
return {
server: {
proxy: {
"/start": {
target: "https://someremoteurl.com",
secure: false,
changeOrigin: true,
configure: (proxy) => {
proxy.on("proxyRes", (proxyRes, req, res) => {
const chunks = [];
proxyRes.on("data", (chunk) => chunks.push(chunk));
proxyRes.on("end", () => {
const buffer = Buffer.concat(chunks);
const encoding = proxyRes.headers["content-encoding"];
if (encoding === "gzip" || encoding === "deflate") {
zlib.unzip(buffer, (err, buffer) => {
if (!err) {
let remoteBody = buffer.toString();
const modifiedBody = remoteBody.replace() // do some string manipulation on remoteBody
res.write(modifiedBody);
res.end();
} else {
console.error(err);
}
});
}
});
});
},
},
},
},
};
});
Everything works as expected modifiedBody is of needed shape.
However the server doesn't return the modified response, it retuns the initial html that the "https://someremoteurl.com" url served.
With the following code the response is "correctly" changed:
proxyRes.on("end", () => {
res.end('<h1>Some Test HTML</h1>')
});
But this wouldnt work for me, as i need to read the response first, unzip it, modify it and only then send back.
To me it looks like the proxied response is streamed, but dev server doesn't wait for the response to first finish streaming, running transformations and only then serving the desired document.
Any idea how can i achieve the desired result?
As Vite uses the http-node-proxy lib under the hood i had to look fo the answer in their documentation. I found that selfHandleResponse option needs to be true in order to serve your modified response.
Setting that option solved my question.

How to access a value within JSON body response (using Request module to make GET requests to an external API)

I am using Express and Node.js to create a simple API that pulls data from an external source. Upon running the code in the block below and entering my /api/posts/tech endpoint into Insomnia I receive a response body with a JSON object that looks like:
{
"posts": [
{
"author": "Rylee Paul",
"authorId": 9,
"id": 1,
"likes": 960,
"popularity": 0.13,
"reads": 50361,
"tags": [
"tech",
"health"
]
},
{
"author": "Zackery Turner",
"authorId": 12,
"id": 2,
"likes": 469,
"popularity": 0.68,
"reads": 90406,
"tags": [
"startups",
"tech",
"history"
]
},
{
"author": "Elisha Friedman",
"authorId": 8,
"id": 4,
"likes": 728,
"popularity": 0.88,
"reads": 19645,
"tags": [
"science",
"design",
"tech"
]
}
]
}
I need to GET just the value of "posts" within the body response so I can manipulate the order of the objects within that array.
I am using the "request" node module to complete my GET request.
Here is the code in my index.js file for this basic Express API:
const express = require('express');
const bodyParser = require('body-parser');
var request = require('request');
const { response } = require('express');
// set up express server connection to the local port
const app = express();
const port = process.env.PORT || 3001
// using body parser to parse the json data
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.get('/api/posts/:tag', (req, res) => {
const { tag } = req.params;
request(`https://api.hatchways.io/assessment/blog/posts?tag=${tag}`, function (error, response, body) {
if (!error && response.statusCode == 200) {
// returns raw response data
res.send(body);
} else
console.log(error);
});
});
app.listen(port, () => {
console.log(`server is live on port: ${port}`);
});
In other projects I have been able to just affix the key value with a period like this: res.send(body.posts) rather than returning res.send(body). But that sends an error message "No body returned for response".
Perhaps it has something to do with the body-parser or request modules that I just haven't figured out. Any help or insight will be appreciated. Thank you!
You can easily do this by parsing it as JSON, as that's what the API is returning.
JSON is a simple structure for data that the computer interprets.
Here is a working version of your code:
request(`https://api.hatchways.io/assessment/blog/posts?tag=${tag}`, function (error, response, body) {
if (!error && response.statusCode == 200) {
// returns raw response data
let struct = JSON.parse(body);
res.send(struct.posts);
} else
console.log(error);
});

Trying to send proper header with Express, Tedious, while accessing Azure SQL

My backend gets a request to get records from an Azure SQL db. To manage this requests I'm using Express in Nodejs, and Tedious (to connect to DB). When the request to the appropriate route comes in, Tedious opens the connection with db, queries it, and it should send the response back to frontend.
However, the code responds before I have an answer with from the db, and thus when I go to send the real (the actually desired) response, Express tells me it already sent headers back (the dreaded: 'Cannot set headers after they are sent to the client').
After debugging quite a bit (using several console.log(JSON.stringify(resp.headersSent)); ) to see when was the response actually sent, I noticed that it's sent the moment I connect with Azure (see below).
I'm not sure if I'm missing something (though I already checked the documentation for all those programs quite a bit), but how can I control when the response is sent? Or, is there another way of doing this.
I omitted several of the other routes for brevity. Other routes work fine and thus I know code connects well to Azure db, and frontend does query backend correctly. Help is appreciated. Thank you.
const express = require('express');
const cors = require('cors');
const Connection = require('tedious').Connection;
const Request = require('tedious').Request;
const config = {
authentication: {
options: {
userName: "xxxx",
password: "xxxx"
},
type: 'default'
},
server: "xxxx",
options: {
database: "xxxx",
encrypt: true
}
};
const app = express();
app.use(express.json({type: '*/*'}));
app.use(cors({ origin: '*' }));
app.get("/allproj/", function (req, resp) {
const q = `select Title, Report_Date, Project_Number, Phase_Code, Items_No, PId from projec order by PId desc`;
let ansf = [];
const connection = new Connection(config);
connection.on('connect', (err, connection) => {
if (err) {
console.log(err);
} else { //this is the moment the headers are sent,
//seemingly with positive response from connection
queryItems(q);
}
});
queryItems = (q) => {
request = new Request(q, function (err, rowCount) {
if (err) {
console.log(err);
} else {
console.log(rowCount + ' rows pulled');
connection.close();
}
});
request.on('row', function(columns) {
let ans = [];
columns.forEach(function(column) {
ans.push(column.value);
if (ans.length === 6) { // I know each row is 6 cols long
ansf.push(ans);
ans = [];
}
});
console.log('ansf length: ' + ansf.length);
resp.send({ ansf }); // This is the response I would like to return
});
request.on('done', function(rowCount) {
console.log(rowCount + ' rows returned');
connection.close();
});
connection.execSql(request);
};
resp.redirect("/");
});
app.listen(3000, process.env.IP, function() {
console.log("Started OK...");
});
Remove resp.redirect("/");
As it is already transferring your request to "/" and when control come at resp.send({ansf}), It gives you error.

Issues writing response actions in firebase functions

I have a function in index.js and I'm trying to resolve an account id from a response on an API. The original response is the following:
{
"data": {
"user": null,
"account": {
"id": 865,
"email": "mitch#gmail.com",
"plan_identifier": "dnsimple-business",
"created_at": "2018-06-24T00:55:29Z",
"updated_at": "2018-06-24T00:56:49Z"
}
}
}
And my code is the following:
exports.dnsCheckAuthorization = functions.https.onRequest((req, res) => {
cors(req, res, () => {
dnsimple.identity.whoami().then((response) => {
return res.status(200).send(response.data.account.id);
}).catch(error => (
res.status(500).send(error)
))
});
});
Finally, the error I receive in PostMan is the following:
Error: could not handle the request
And the error in the Firebase log is the following:
Function execution took 519 ms, finished with status: 'response error'
I've tried literally everything I can think of to get just the ID returned by this function and just can't figure it out. What am I missing?
UPDATE
I got this to work with the following code. Not quite what I want though. I want to return just the account id.
exports.dnsCheckAuthorization = functions.https.onRequest((req, res) => {
cors(req, res, () => {
dnsimple.identity.whoami().then((response) => {
var accountID = response.data.account.id;
console.log('account id is', accountID);
return res.status(200).json({id: accountID});
}).catch(error => (
res.status(500).send(error)
))
});
});
res.send() is an express only function. So it may not work if you did not create your server using express. Instead, you could use try something like this -
res.status(200).end(response.data.account.id)

Nodejs is not receiving any code from Flask app.

I am really new in node js and a little bit more experienced in flaks. I am trying to connect a nodejs backend with a flask api. Basically I am sending a file that was uploaded in the nodejs app for processing (converting to another format) to my flask app.
For sending the data I am using request. In this way:
app.post('/converttest', uploader.single('file'), function(req,res){
var file = req.file,
result = {
error: 0,
uploaded: []
};
flow.exec(
function() { // Read temp File
fs.readFile(file.path, this);
},
function(err, data) { // Upload file to S3
var formData = {
file: data,
};
requestPack.post({url:'http://127.0.0.1:5000/api/resource/converter', formData: formData});
},
function(err, httpResponse, body) { //Upload Callback
if (err) {
return console.error('upload failed:', err);
}
res.redirect('/console');
});
});
Then I am receiving the file for processing in the flask app, like:
#app.route('/api/resource/converter', methods = ['POST','GET'])
def converter_csv():
if request.method == 'POST':
f = request.form['file']
if not f:
abort(400)
print('-----Converting-------')
file = open("temp/converting.txt","w")
file.write(f)
#....conversion process...
# Finish the process
return Response(converted_file,status=200)
In my console for the localhost of the flask app, I am getting:
127.0.0.1 - - [09/Aug/2017 15:47:59] "POST /api/resource/converter HTTP/1.1" 200 -
However my nodejs app did not receive any response. It just got frozen.
I appreciate any orientation anyone can give me. Thanks.
I think flow.exec is not in proper order
router.post('/converttest', uploader.single('file'), function(req, res) {
var filePath = req.file.path;
fs.readFile(filePath, 'utf8', function(err, data) { //change format reading as required
try {
formData = {file:data}
requestPack.post({url:'http://127.0.0.1:5000/api/resource/converter', formData: formData});
} catch(err) {
return console.error('upload failed:', err);
res.redirect('/console')
}
fs.unlink(filePath);}); });
I ended up using requestify. Seems like they make it a little bit easier for beginners like me:
var requestify = require('requestify');
app.get('/convertupload', function(req,res){
res.render('pages/convertupload');
});
app.post('/converttest', uploader.single('file'), function(req,res){
var file = req.file,
result = {
error: 0,
uploaded: []
};
flow.exec(
function() { // Read temp File
fs.readFile(file.path,this);
},
function(err, data) { // Upload file to S3
var formData = {
file: data
};
requestify.post('http://127.0.0.1:5000/api/resource/converter', {
form: formData
})
.then(function(response) {
// Get the response body (JSON parsed or jQuery object for XMLs)
console.log(response)
response.getBody();
});
res.redirect('/login');
});
});

Resources