Issues writing response actions in firebase functions - node.js

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)

Related

Getting XMLHttpRequest error from HTTP GET request on unformatted JSON

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);

I cant get a response from a POST request with an array in the body, Using NodeJS / Axios and Postman

This is a course quiz and this is the most basic information I need in order to create a React app. But while the endpoint URL is correct, the page "/products" returns a "400" error when I try to request the product list. The instructions I'm given are:
Obtain a list of products with
Route: /products
Body: Array
method: POST
{
"product-codes": [
"98798729",
"84876871",
"29879879",
]
}
My index.js
...
app.post(`/products`, async (req, res) => {
try {
const response = await axios.post(`${apiURL}/products`);
// console.log(response.data);
res.status(200).json(response.data);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
in Postman
I use http://localhost:4000/products
and pass a Body / Raw /JSON:
{
"product-codes": [
"98798729",
"84876871",
"29879879",
]
}
But I can't get in! I am not seeing something obvious because this is the entry point to a very long and complex quiz. Thanks
What I see from the code is a recursive long call.
app.post(`/products`, async (req, res) => {
try {
const response = await axios.post(`${apiURL}/products`); // calling the same end point
// console.log(response.data);
res.status(200).json(response.data);
} catch (error) {
res.status(400).json({ message: error.message });
}
});
You should do something like this:
app.post(`/products`, async (req, res) => {
// do your logic
// when you pass body from postman on this endpoint
// you will receive the body here and save it to DB
// or do the manipulation and then send back the response
res.status(200).json(req.body.data);
});
I highly recommend you to first follow through some tutorials to understand how API works and how to create simple API using Node.js.

Node GET by ID API

I have follow the instructions to create a NODE API here.
I'm trying to have a few endpoints with a NODE app to serve data to my React UI.
The database is mongodb where I have a collection for 'stores'.
I have 2 GET calls:
One to retrieve all stores
One to retrieve a store by ID
Node app.js:
app.get('/viewstores', (request, response) => {
storesCollection.find({}).toArray((error, result) => {
if (error) {
return response.status(500).send(error);
}
response.send(result);
});
});
app.get("/viewstores/:id", (request, response) => {
storesCollection.findOne({ "_id": new ObjectId(request.params.id) }, (error, result) => {
if(error) {
return response.status(500).send(error);
}
response.send(result);
});
});
I make my API calls from axios in React.
If I make a call to the first API to retrieve all stores, there no problem at all, but if I try to make the API call by ID, I still get all stores from the first API.
It seems that I am not able to target the GET by ID api.
React app
React.useEffect(() => {
axios.get('http://localhost:5000/viewstores', {
params: { _id: params.storesid}
})
.then(({data}) => {
console.log("DATA ==> ", data)
})
.catch(error => console.log("ERROR API GET ==> ", error))
}, [])
MongoDB store sample:
_id: ObjectId("12345")
businessname:"ABC"
businessaddress:"address abc 1"
Any idea why when I try to call the GET by ID I always get back the whole collection?
Thanks in advance.
Joe.
Assume params.storesid is 12345,
your current React code sends requests to http://localhost:5000/viewstores?_id=12345, and the route /viewstores is reached. To reach the /viewstores/:id route, the URL should be something likes http://localhost:5000/viewstores/12345 then Express will capture the 12345 part in the URL to request.params.id. You can try the code below:
React.useEffect(() => {
axios.get(`http://localhost:5000/viewstores/${params.storesid}`)
.then(({data}) => {
console.log("DATA ==> ", data)
})
.catch(error => console.log("ERROR API GET ==> ", error))
}, [])
You can read about Express route parameters in the official document.

Client side can't fetch server response

The Problem
I deployed a create-react-app webapp to aws ec2. It's used to display data from a database and send data to it. I use ExpressJS, CORS and MySQL.
With the following code i fetch the corresponding URL and the server.js sends back the database content. Until here, everything works fine.
getBets = _ => {
fetch("http://ec2***.amazonaws.com
.then(response => response.json())
.then(response => this.setState({bets: response.data}))
.catch(err => console.error(err))
};
The problem begins when sending data to the database with the following code:
addBet = _ => {
const { bet } = this.state;
fetch(`http://ec2***.amazonaws.com/bets/add?name=${bet.person_name}&bet=${bet.time_bet}`)
.then(response => response.json())
.then(this.getBets)
.catch(err => console.error(err))
};
On click the addBet-function populates the db, but in chrome I following error:
GET http://ec2***.amazonaws.com/bets/add?name=Peter%20Pan5&bet=10:17%205 net::ERR_EMPTY_RESPONSE
and
TypeError: Failed to fetch
Regarding chrome dev-tools, the first error corresponds to the fetch in the addBet function and the second error to the catch part.
On the server side I've the following code for processing the fetch:
app.get("/bets/add", (req, res) => {
const {name, bet} = req.query;
const INSERT_BET = `INSERT INTO bets (name, bet, timestamp) VALUES("${name}", "${bet}", CURTIME())`;
connection.query(INSERT_BET, (err, res) => {
if (err) {
return res.send(err);
}
else {
return res.send("succesfully added your bet");
}
})
});
I want to mention, that the res paramter in the app.get part is unused. That tells me my IDE.
After a lot of hours digging deeper in the topics of expressJS and the fetch api, I guess, that the app.get part doesn't send a response to the server. But the fetch need some response.
My Question
How do I have to change the code in the app.get part to send a proper response back to the server?
AND
Am I right with my guess?
In MYSQL when you do an insert query you get back err,results and fields in the callback function like this:
connection.query('INSERT INTO posts SET ?', {title: 'test'}, function (error,
results, fields) {
if (error) throw error;
console.log(results.insertId);
});
You have used the parameter res for result and then you have used res.send() which now corresponds to that res parameter in the callback function and not the res object.Rewrite it like this:
app.get("/bets/add", (req, res) => {
const {name, bet} = req.query;
const INSERT_BET = `INSERT INTO bets (name, bet, timestamp) VALUES(?,?,?)`;
connection.query(INSERT_BET,[name,bet,CURTIME()] ,(err, result) => {
if (err) {
return res.send(err);
}
else {
return res.send("succesfully added your bet");
}
})
});
I have also used prepared statement in place of normal sql queries. These are used to prevent sql injections. I hope it will work now.

How to test an API that calls another external API in node

I'm using mocha, chai, and chai-http to test my simple API that routes calls from Slack to Habitica, integrating these two services.
I'm trying to start by creating tests, but I'm facing this issue: when I call my API, the code returns before the external API call. This is the code of the test:
var chai = require("chai");
var chaiHttp = require("chai-http");
var server = require("../src/app/index");
var should = chai.should();
chai.use(chaiHttp);
describe("/GET list", () => {
it("it should return a list of user\'s tasks", (done) => {
chai.request(server)
.post("/habitica")
.type("urlencoded")
.send({text: "list"})
.end((err, res) => {
res.should.have.status(200);
res.body.should.be.a("object");
res.body.should.have.property("success").eql("true");
done();
});
});
});
This is the code that is been called by the test:
app.post("/habitica", server.urlencodedParser, function(req, res) {
if (typeof req.body !== "undefined" && req.body) {
switch(req.body.text) {
case "list":
request({
url: GET_TASKS,
headers: { "x-api-user": process.env.HABITICA_USERID, "x-api-key": process.env.HABITICA_APITOKEN }
}, function (apiError, apiResponse, apiBody) {
if (apiError) {
res.send(apiError);
} else {
res.send(apiBody);
}
});
break;
default:
res.send({
"success": "false",
"message": "Still working on tasks creation"
});
}
}
});
This code returns before the call to Habitica return any value. This is the result of "npm test":
/GET list
1) it should return a list of user's tasks
0 passing (2s)
1 failing
1) /GET list
it should return a list of user's tasks:
Uncaught AssertionError: expected {} to have property 'success'
at chai.request.post.type.send.end (test/app.js:17:34)
at Test.Request.callback (node_modules/superagent/lib/node/index.js:706:12)
at IncomingMessage.parser (node_modules/superagent/lib/node/index.js:906:18)
at endReadableNT (_stream_readable.js:974:12)
at _combinedTickCallback (internal/process/next_tick.js:80:11)
at process._tickCallback (internal/process/next_tick.js:104:9)
I've already searched in a lot of forums and sites:
Some people say that I shouldn't test code I don't own: this makes a lot of sense, but what should I've been testing since it is just a simple integration service?
Some people say that I should mock the external api result: but I won't been testing anything since, again, it is just an integration.
How can I solve this?
Thanks in advance.
You should mock the calls to external API & test how your app should behave in case of failure or success after calling the external API.
You can test different scenarios as follows
describe("/GET list", () => {
// pass req.body.text = 'list'
describe("when task list is requested", () => {
describe("when task list fetched successfully", () => {
// in beforeEach mock call to external API and return task list
it('returns tasks list in response', () => {
})
}),
describe("when error occurs while fetching task list", () => {
// in beforeEach mock call to external API and return error
it('returns error in response', () => {
})
})
}),
// when req.body.text != 'list'
describe("when task list is not requested", () => {
it('returns error in response', () => {
})
})
})

Resources