Socket io in nest js not receiving acknowledgements - node.js

I have a little problem with acknowledgements function in socket io using nestjs framework.
Normally, with the code below, the data parameter in the onStartSearch function should contain the data and ack function to notify the frontend with. But the data params contain only data sent from the frontend.
Backend
#SubscribeMessage('startSearch')
async onStartSearch(socket, data) {
logger.info('User ' + socket.id + ' startedSearch');
logger.debug(data);
if (Joi.validate(data, requestSchema).error) {
return ack({
status: 'error',
reason: 'invalid params format',
});
}
Frontend
public sendMessage(payload: IChatMessage) {
this.socket.emit('message', payload, function(test) {
console.log("acckk received")
});
}

If you just return plain data, the acknowledgment (callback) function will automatically be called by nest:
#SubscribeMessage('startSearch')
async onStartSearch(socket, data) {
if (Joi.validate(data, requestSchema).error) {
return {
status: 'error',
reason: 'invalid params format',
};
}
}
But you can also work without the callback function (e.g. when working with native sockets which don't support the callback function):
The #SubsribeMessage methods can also return an Observable<WsResponse<any>>. WsResponse contains the event name and the payload {event: string, data: any}.
#SubscribeMessage('startSearch')
async onStartSearch(socket, data): Observable<WsResponse<any>> {
if (Joi.validate(data, requestSchema).error)
return of({event: 'searchResponse', data});
}
Then just listen to the response on your client:
socket.on('searchResponse', function (data) {
console.log('event', data);
});

Related

how to send fetched data node js

Hello i have a request which fetch some json data from third party API:
request({
url: 'https://api.steampowered.com/IEconService/GetTradeOffers/v1/?key=MYAPIKEY&get_sent_offers=1&active_only=1&format=json',
json: true
}, (err, responser, body, undefined) => {
tradeItems = JSON.stringify(body.response['trade_offers_sent'][0].items_to_give);
});
How can i send tradeItems fetched data to offer.addTheirItems value?
client.on('webSession', function(sessionID, cookies) {
manager.setCookies(cookies, function(err) {
if (err) {
console.log(err);
process.exit(1);
return;
}
let offer = manager.createOffer("https://steamcommunity.com/tradeoffer/new/?partner=123456789&token=1234");
offer.addTheirItems();
offer.setMessage("");
offer.send(function(err, status) {
if (err) {
console.log(err);
return;
}
First, that's are javascript's async issue.
The solution is in many ways.
change the request function to async function. and make tradeItems variable to outside from request function.
I recommend request-promise module
move below codes to in upper code's callback function.
This is a simple answer because your sample code is separated into two parts.

Send response to client after Promise resolves - Adonis, 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) { ... }

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.

How to send response back to client using socket.io?

I have socket.emit call from client to server in response i want to have filename to the client that is not happening with below code not sure what is implemented wrong any idea, I do not see any error. How can i get response fro server using socket.emit ?
client.js
socket.emit('startRecording',function (response) {
console.log('start recording emit response',response);
});
server.js
socket.on('startRecording',function () {
var response;
logsRecording(function (filename) {
response = filename;
return response;
//socket.emit('filename',filename);
});
To acknowledge the message, your handler for the startRecording event needs to accept an acknowledgement callback as a parameter. You can then call that with your desired data. See Sending and getting data (acknowledgements)
socket.on('startRecording',function (socket, ackFn) {
var response;
logsRecording(function (filename) {
ackFn(filename);
});
});
Alternatively, you could add a listener for that filename event you have commented out, in the client.js:
socket.emit('startRecording');
socket.on('filename', function(filename) {
console.log('Filename received: ' + filename);
});
It might be helpful to run through Get Started: Chat application starting at the heading "Integrating Socket.IO" to get a more general understanding of Websockets.
Your server code should look like this:
socket.on('startRecording',function (callbackFn) {
var response;
logsRecording(function (filename) {
callbackFn(filename);
});
If you want to pass in data from your client:
socket.emit('startRecording', {someData: 'value'}, function (response) {
then server will be :
socket.on('startRecording',function (dataFromClient, callbackFn) {
Thank you for this helpfull hint
Here an 2020 "call" example call possible to use with moleculer microservices with four arguments:
The server responds in the callback function with two arguments err and res for the angular promise.
Angular 9 socket io
protected call(method: string, param?: any) {
try {
return new Promise((resolve, reject) => {
this.socket.emit("call", method, param, (err: any, res: unknown) => {
console.log(res);
if (err) { return reject(err); }
return resolve(res);
});
});
} catch (err) {
console.error(err);
}
}
Socket IO server response
socket.on('call', function(method, param, callbackFn){ // call method, param,
console.log(param);
switch (method) {
case "test":
console.log("test detected");
callbackFn(null , {name:"test",email:"test"});
break;
}
});

Avoiding multiple verification for Twitter API (node.js)

I'm trying to create a basic app in node.js that a) tracks a keyword in twitter and temporarily stores messages relating to that keyword, b) after enough messages have been accumulated, return it to the user. I'm using the ntwitter library.
I've a basic long polling system implemented on my client and server side, but I'm having some trouble on verification. The way I set it up currently, it verifies the user each time /api/streamfeed is called, so potentially every 30sec (since I have a 30s timeout schedule) before checking the stream. I'm thinking this will get me into trouble since I believe verification is rate-limited? Is there a way to check whether I'm verified without having to ping Twitter's API (perhaps store a boolean after the first attempt)?
Client side:
//upon receiving a response, poll again
function getStreamFeed() {
console.log('calling getStreamFeed');
$http.get('/api/streamfeed').success(function(data) {
console.log(data)
getStreamFeed();
});
};
setTimeout(getStreamFeed, 1000);
Server side:
app.get('/api/streamfeed', function(req, res) {
/*
...
polling code
...
*/
twit.verifyCredentials(function(err, data) {
if (err) res.send(404);
twit.stream('statuses/filter', {
track: 'justin bieber'
}, function(stream) {
stream.on('data', function(data) {
console.log(data.text)
messages.push(data.text);
});
})
});
});
I'd send the credentials back and resend them again... this could be a bool, or actual credentials to use. these aren't your private keys or anything, only the user's.
could also be sent in headers and cookies and properly hashed etc.
this just simply shows a pattern that should work.
client side:
function getStreamFeed(credentials) {
//upon receiving a response, poll again
console.log('calling getStreamFeed');
var url = '/api/streamfeed';
if (credentials) {
url += '&credentials=' + credentials;
}
$http
.get(url)
.success(function(data) {
console.log(data)
getStreamFeed(true);
});
};
setTimeout(getStreamFeed, 1000);
Server side:
app.get('/api/streamfeed', function(req, res) {
function twitStream () {
twit.stream('statuses/filter', {track: 'justin bieber'}, function(stream) {
stream.on('data', function(data) {
console.log(data.text)
messages.push(data.text);
});
}
}
var credentials = req.query.credentials;
if (credentials) {
twitStream()
}
twit.verifyCredentials(function(err, data) {
if (err) res.send(404);
twitStream()
});
});

Resources