Send response to client after Promise resolves - Adonis, Node.js - node.js

I have some promise
getSomeInfo(data) {
return new Promise((resolve, reject) => {
/* ...some code... */
someObject.getData((err, info) => {
if (info) {
resolve(info)
}
else {
reject("Error")
}
})
})
}
I use this promise and want to send response to client from Controller (AdonisJS):
async create ({ request, response }) {
this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
})
}
Why response is not work?

Simply do this.
async create ({ request, response }) {
const info = await this.getSomeInfo(data)
console.log(info)
response.status(201).json({
code: 201,
message: "Data received!",
data: info
})
}

When marking a function as async the function must return a Promise, this can be done explicitly.
async create({ request, response }) {
return this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
})
}
Or implicitly using the await keyword.
async create({ request, response }) {
const info = await this.getSomeInfo(data)
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
})
}

If your console.log(info) inside of create() works and shows the data you want, but the response.status(201).json(...) does not send a response, then I can see the following possibilities:
You've already sent a response to this request (and thus cannot send another one)
The .json() method is having trouble converting info to JSON (perhaps because of circular references) and throwing an exception.
You aren't passing the arguments request and response properly and thus response isn't what it is supposed to be.
You can test for the second case like this:
create ({ request, response }) {
this.getSomeInfo(data).then(info => {
console.log(info) // It's work, i get the data from promise
response.status(201).json({ // but this is not work
code: 201,
message: "Data received!",
data: info
});
}).catch(e => {
console.log("Error in create()", e);
response.sendStatus(500);
});
}
Also, there is no reason for this method to be declared async as you don't show that you're using await or any of the features of an async function.
In the comments, you say that this function is called directly by a router (I assume an Express router). If that's the case, then the function arguments are not declared properly as they come as two separate arguments, not as properties of an object. Change the function declaration to this:
create (request, response) { ... }

Related

didn't get response when using res.write() inside of the fetch function

I am using this <res.write()> ==> https://nodejs.org/api/http.html#responsewritechunk-encoding-callback (in nodejs)
and using this fetch ==> https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
My situation is that I didn't see any response when using the res.write() function inside of the fetch function. for example, in the below backend code, I tried to put res.write("456") inside of the first then function below fetch, but I only see 123 returned in the frontend, no 456.
res.write("123");
fetch('http://example.com/movies.json')
.then((response) => {response.json(), res.write("456")})
.then((data) => console.log(data));
I have searched Google for a while, but didn't see anything related. My guess is that this could be because of async usage.
appreciate if someone can give suggestions.
===== update =====
res is express's res obj
async sendText(res: Response){
res.write("123")
fetch('http://example.com/movies.json')
.then((response) => {return response.json()})
.then((data) => {
console.log(data);
res.write('456');
res.end();
});
}
seeing behavior: only see 123 in the frontend.
VS
async sendText(res: Response){
res.write("123")
await fetch('http://example.com/movies.json')
.then((response) => {return response.json()})
.then((data) => {
console.log(data);
res.write('456');
res.end();
});
}
seeing behavior: can see both 123 and 456 in the frontend.
I never use await and .then together before, not fully understand the difference. searching the web rn.
You aren't checking for any errors or an unsuccessful response so response.json() may be failing, preventing anything after it from executing.
Something like this should work better for you
async sendText (res: Response) {
res.write("123");
const response = await fetch("http://example.com/movies.json");
// check for an unsuccessful response
if (!response.ok) {
const error = new Error(`${response.status} ${response.statusText}`);
error.text = await response.text();
throw error;
}
const data = await response.json();
res.write("456");
res.end(); // finalise the HTTP response
console.log(data); // log data for some reason ¯\_(ツ)_/¯
};
This will return a promise that resolves with undefined or rejects with either a networking error, HTTP error or JSON parsing error.
When calling it, you should handle any rejections accordingly
app.get("/some/route", async (res, res, next) => {
try {
await sendText(res);
} catch (err) {
console.error(err, err.text);
next(err); // or perhaps `res.status(500).send(err)`
}
});
I never use await and .then together before
Nor should you. It only leads to confusing code.

How do I return a promise in a Node.js view?

In my backend, I make an api call to a third party website that returns a promise. I need to pass that promise to an html template, however using JSON.stringify() in the backend and then JSON.parse() in the frontend didn't work. I received this error:
JSON.parse: unexpected character at line 1 column 2 of the JSON data
So I've decided to change my strategy and do a fetch request from the html template to my Node.js server. This is my current code:
async function getItems() {
return await api.Items
...
}
app.get('/getItems', function(req, res){
res.end(Buffer.from(getItems()));
});
The problem I've run into is that I can't return a promise with res.end, even with Buffer:
TypeError [ERR_INVALID_ARG_TYPE]: The first argument must be of type string or an instance of Buffer, ArrayBuffer, or Array or an Array-like Object. Received an instance of Promise
I also tried:
app.get('/getItems', function(req, res){
getItems().then(result=>
{
res.end(result);
}
).catch(err =>
{
res.status(504);
}
)
});
However the connection times out with a 504 status.
How do I make the view return a promise?
Edit: In the second example, where I get the result of the promise, it doesn't time out if I set it to res.end('5').
Edit again:
const items = fetch('127.0.0.1/api');
console.log(items);
for (const item of items) { console.log(item); }
Response { type: "basic", url: "127.0.0.1/api", redirected: false, status: 200, ok: true, statusText: "OK", headers: Headers, body: ReadableStream, bodyUsed: false }
Uncaught (in promise) TypeError: items is not iterable
You have to wait for the promise to resolve and THEN send the response. There are a couple ways to do it:
app.get('/getItems', function(req, res){
getItems().then(result => {
res.json(result);
}).catch(err => {
console.log(err);
res.sendStatus(500);
});
});
This would send your items as JSON which is presumably more useful than sending a buffer.
Or, you can use async/await like this:
app.get('/getItems', async function(req, res){
try {
const result = await getItems();
res.json(result);
} catch(err) {
console.log(err);
res.sendStatus(500);
}
});

Cannot set headers after they are sent to the client error upon deleting document and executing post middleware

I have a problem related to, most likely, double execution of the delete query on a document. My suspicion is the issue lies in the middleware executing after deleting the document, could that be the case? The error I get says:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
[0] at ServerResponse.setHeader (_http_outgoing.js:558:11)
[0] at ServerResponse.header
...
Axios call on frontend:
axios.delete(`http://localhost:5000/columns/delete/${columnId}`, {
headers: {
'Authorization': `Bearer ${localStorage.getItem('token')}`,
}
}).then(() => {
dispatch({type: ActionTypes.DeleteColumn, payload: columnId})
}).catch((err) => {
console.log(err);
});
Mongoose method on backend:
export const deleteColumn = asyncHandler(async(req, res) => {
try {
await Column.findByIdAndDelete(req.params.columnId);
res.json();
} catch(err) {
res.status(404).json({message: "Column not found"});
throw new Error('Column not found');
}
});
Finally, the middleware that clears all the child elements in a cascade:
columnSchema.post('findOneAndDelete', async function(res) {
const columnId = res._id;
const childIssues = await Issue.find({columnId: columnId});
// array of promises is passed to Promise.all to resolve concurrently
Promise.all(
childIssues.map(async column => {
await Issue.findByIdAndDelete(column._id)
})
);
});
I know this code is far from perfect, especially the middleware part. I'm still learning the related concepts and I find it particularly hard to wrap my head around asynchrounous calls, so I will be more than happy for any feedback and help.

NodeJs, Axios: Value of variable not updating with response from xhr request

I am making an xhr request in nodejs using Axios. And I am trying to save a value from this response to a variable, but it isn't working and I am not sure why.
Code:
let redirectUrl = 'placeholder'
axios.get(url)
.then(res => {
redirectURL = res.url
console.log(res.url, "HERE")
})
.catch(err => console.log(err))
return res.render('index',{url: redirectURL})
I have tried declaring redirectUrl in both global as var and local scope, but the value isn't changing, on index when I console log this value, it logs placeholder. index is index.jsx as I am using jsx as the template engine.
Please let me know if I should provide any more information.
Thank You in advance.
axios.get is an async call, so your return will use the original (placeholder) value instead of the new one. Normally to solve this kind of asynchronicities i use async/await
async function getRedirectURL(url) {
try {
const res = await axios.get(url);
// Note that if you want some return value from axios call you access it by res.data
console.log('res.url :', res.url, ". res.data:", res.data);
return res.render('index',{url: res.data.url});
} catch (err) {
console.log(err);
throw err;
}
}
Axios is a promise-based HTTP client, So when javascript engine executes your code, it moves axios call to web API from callstack (line# 2) and then it starts to execute the last line return res.render('index',{url: redirectURL}) where redirectURL value is placeholder. You have to write all the logic in promise then method, like
axios
.get(url)
.then((data) => res.render("index", { url: data.url }))
.catch((err) => {
// error response
console.log(err);
});

Meteor : Retrieve value in Meteor.call which calls a server method having promise

I have an internally maintained npm package myNpmPackage which exports a function (for e.g. fnTestMicroSerConn ) as below:
const rp = require('request-promise-native')
exports.fnTestMicroSerConn = function () {
return new Promise(function(resolve, reject) {
var options = {
method: 'GET',
uri : "http://example.net",
resolveWithFullResponse: true,
}
rp(options)
.then(function (response) {
if (response.statusCode !== 200){
console.error("http not 200 but : ",response.statusCode)
resolve(false)
} else {
console.info("connected successfully : "+response.body)
resolve(response)
}
})
.catch(function (err) {
console.error("Error in establishing connectivity : ",err)
resolve(false)
})
})
}
I then need to call the above exported function from a Meteor method like so:
import { Meteor } from 'meteor/meteor';
import myNpmPackage from 'myNpmPackage';
Meteor.methods({
foo: function () {
myNpmPackage.fnTestMicroSerConn().then(function (response){
console.log(" My response: ",response.body);
return(response.body)
})
}
});
console.log(" My response: ",response.body); gets executed successfully and I can see the expected value in the server console log. So till here it's good.
However, now I want to pass the value of response.body to the client side. In short, when I do below on the client :
Meteor.call("foo", function (err, response) {
console.log("calling foo");
if(!err){
console.log("response : ",response);
} else {
console.log("err : ",err);
}
})
Unfortunately, currently I am getting undefined on the client for console.log("response : ",response);
Note: I am using the Meteor Promise package from here
Let me know if any more details are needed or any thing is unclear. I am very new to the Promise style of coding, hence, this can sound as a noob question.
Meteor methods called from clients by Meteor.call run synchronously to prevent clients from pending, even if a callback is supplied.
Your foo method does not wait for that promise inside. It runs past fnTestMicroSerConn() call without hesitation and ends up with no more statement to execute, returning undefined as a result. By the time the promise resolved and logged the expected message on the server console, the method had been exited.
To get resolved/rejected result of that promise, you can return the promise from the method to the caller, and the client would be able to respond to the promise.
Meteor.methods({
foo: function () {
return myNpmPackage.fnTestMicroSerConn();
}
});
Meteor.call("foo")
.then( response => console.log("My response: ", response.body) )
.catch( err => console.log("err : ",err) );
Meteor methods is powerful. The API documentation of methods contains much information and is worth mastery.

Resources