How to send MQTT "message" event back to REST body? - node.js

I'm currently having problems figuring out how to capture my MQTT message event back to my REST API body which is written in NodeJS. My current setup is App -> NodeJS REST API -> MQTT broker inside RPi 3.
This is my MQTTHandler.js class where I have put all my reusable MQTT functions
const mqtt = require('mqtt')
class MQTTHandler {
constructor (host) {
this.client = null
this.host = host
}
connect () {
this.client = mqtt.connect(this.host)
this.client.on('error', function (err) {
console.log(err)
this.client.end()
})
this.client.on('connect', function () {
console.log('MQTT client connected...')
})
// I need this to send message back to app.js
this.client.on('message', function (topic, message) {
if (!message.toString()) message = 'null'
console.log(JSON.parse(message.toString()))
})
this.client.on('close', function () {
console.log('MQTT client disconnected...')
})
}
subscribeTopic (topic) {
this.client.subscribe(topic)
}
unsubscribeTopic (topic) {
this.client.unsubscribe(topic)
}
sendMessage (topic, message) {
this.client.publish(topic, message)
}
}
module.exports = MQTTHandler
And below is a short snippet of my app.js
const MQTTHandler = require('./mqtt.handler')
...
var mqttClient = new MQTTHandler('mqtt://127.0.0.1')
mqttClient.connect()
app.get('/hello', function (req, res) {
mqttClient.subscribeTopic('topic')
mqttClient.sendMessage('topic', 'hello world')
// I need to return the MQTT message event here
// res.json(<mqtt message here>)
res.end()
})
I have already tried using NodeJS' event emitter but it doesn't seem to work. Any help or suggestions would be much appreciated, thank you!

You are trying to mix a synchronous protocol (HTTP) with and asynchronous protocol (MQTT). These 2 paradigm don't easily mix.
When you publish an MQTT message you have no idea how many clients may be subscribed to that topic, it could be zero, it could be many. There is also no guarantee that any of them will send a reply so you need to include a timeout. (You also need to include a request id in the payload so you can coordinate any response with the request as you can't say anything about what order responses may come in.)
Your example code is only using 1 topic, this is very bad as you will end up needing to filter out request messages from response messages. Better to use 2 different topics (MQTT v5 even has a msg header to specify the topic the response should be sent on).
Having said all that it is possible to build something that will work (I will use request and reply topics.
var inflightRequests = {};
// interval to clear out requests waiting for a response
// after 3 seconds
var timer = setInterval(function() {
var now = new Date.now();
var keys = Object.keys(inflightRequests);
for (var key in keys) {
var waiting = inflightRequests[keys[key]];
var diff = now = waiting.timestamp;
// 3 second timeout
if (diff > 3000) {
waiting.resp.status(408).send({});
delete(inflightRequests[keys[key]]);
}
}
},500);
// on message handler to reply to the HTTP request
client.on('message', function(topic, msg){
if (topic.equals('reply')) {
var payload = JSON.parse(msg);
var waiting = inflightRequest[payload.requestId];
if (waiting) {
waiting.res.send(payload.body);
delete(inflightRequests[payload.requestId]);
} else {
// response arrived too late
}
}
});
// HTTP route handler.
app.get('/hello', function(req, res) {
//using timestamp as request Id as don't have anything better in this example.
var reqId = Date.now();
var waiting = {
timeStamp: reqId,
res: res
}
inflightRequests[reqId] = waiting;
var message = {
requestId: reqId,
payload: 'hello world'
}
client.publish('request',JSON.stringify(message));
});

Related

How to listen to socketIO private message in React client?

I have a SocketIO instance in an Express app, that listens to a React client requests. A user can send private messages to a specific person. The server receives the private message, and should dispatch it back to both sender & recipient thanks to the io.to(socketId).emit(content) method.
How to listen to this event in React and update the message array? In order to ease the process, I have created a connectedUsers object, whose keys are mongoDB's user._id, and whose values are the unique socketID generated by socketIO. This way, I can easily address message to specific persons in the client. Once sent, the messages are stored in a MongoDB database.
Here is the back-end. The point of interest is io.on("privateMessage")
const connectedUsers = {};
const socketManager = (io) => {
io.on("identifyUser", (user) => {
if (!([user.id] in connectedUsers)) {
connectedUsers[user.id] = io.id;
}
});
io.on("privateMessage", (data) => {
io.to(connectedUsers[data.recipientId]).emit(data.message);
io.to(connectedUsers[data.senderId]).emit(data.message);
});
io.on("disconnect", () => console.log("user disconnected!"));
};
Here is the listening function in React. Everything works but the "privateMessage" part.
async function getUser(socketId) {
try {
const res = await ax.get(`${serverUrl}/login`);
const socket = io(serverUrl);
socketId.current = socket;
socket.on("connect", () => {
socket.emit("identifyUser", { id: res.data._id });
socket.on("privateMessage", (data) =>
console.log("private message received!", data)
);
});
} catch (err) {
throw new Error(err);
}
}
Thanks for your help!
I think you need to put the socket.on("privateMessage") part outside the socket.on("connect") scope.
React must load all events at the beginning.
The backend side must be responsible for the authorization.
For the client there is connection event, not connect.
Subscription to event privateMessage should be outside connection callback.
This code should work. Hope this helps
import io from 'socket.io-client'
async function getUser(socketId) {
try {
const res = await ax.get(`${serverUrl}/login`);
const socket = io(serverUrl);
socketId.current = socket;
socket.on("connection", () => {
socket.emit("identifyUser", { id: res.data._id });
});
socket.on("privateMessage", (data) =>
console.log("private message received!", data)
);
} catch (err) {
throw new Error(err);
}
}

NodeJS - How to have express wait for a response?

I am working with a very small piece of nodeJS code thats using express. The app receives a payload as a POST and sends it to another method which submits it to kafka. Once this happens, I need to return a response back to express so that it can close the connection based on the status (return a 200 response for example).
// producer.js
// Defined variables
var kafka = require('kafka-node'),
Producer = kafka.Producer;
module.exports = {
sendToProducer: function (payload) {
// Define our kafka settings
var client = new kafka.KafkaClient(),
producer = new Producer(client);
// On producer ready
producer.on('ready', function () {
// Send our payload to our topic
producer.send(payload, function (err, data) {
console.log(data);
// Return our response back to `app.js` so express can handle it.
});
});
// On producer error
producer.on('error', function (err) {
console.log('Producer Error', err)
})
}
};
// app.js
app.post('/payload', function (req, res) {
// Define our route
const payload = req.body;
const route = payload.route;
// Check to see if we have a route
if(!route){
logError('no route');
}
let mappedData = [{ topic: 'test', messages: 'hi... ', partition: 0 }]
// Based on the route
switch(route){
// Receive data
case 'ingest':
producer.sendToProducer(mappedData);
// How can I tell express the status of the above method so that we can close the request? (ex 200 response for example?)
break;
// Handle undefined routes
default:
break;
}
});
In my app.js case statement, how can I wait for a response/callback from producer.sendToProducer(mappedData); so that I can handle the express status appropriately ?
Make sure your sendToProducer() is a promise / async function :
app.post('/payload', async function (req, res) {
// Define our route
const payload = req.body;
const route = payload.route;
// Check to see if we have a route
if(!route){
logError('no route');
}
let mappedData = [{ topic: 'test', messages: 'hi... ', partition: 0 }]
// Based on the route
switch(route){
// Receive data
case 'ingest':
await producer.sendToProducer(mappedData);
// How can I tell express the status of the above method so that we can close the request? (ex 200 response for example?)
break;
// Handle undefined routes
default:
break;
}
});

How to pass full request or response in Redis Bull queue

It may be a wrong way to use bull queue but here is what I want to do:
var Promise = require('bluebird');
var redis = require('redis');
var Queue = require('bull');
var redisClient = redis.createClient(6379);
var pdfQueue = new Queue('msg');
function check(resolve, reject,i) {
console.log('check called');
//Or if it is in Router then I want to send request, response in queue so that I can call them in on complete function
pdfQueue.add('msg',{'msg':'Hello', 'resolve':resolve,'reject':reject}).then(job=>{
console.log('added to the pdf')
});
}
pdfQueue.on('completed', function (job, result) {
//Here I want to call request.send('some msg');
//and resolve('complete');
resolve('final callback');
})
pdfQueue.process('msg',100,function (job,done) {
console.log('process');
done(null,'job done ')
})
function check2 () {
return new Promise(function(resolve, reject){
check(resolve,reject);
})
}
check2().then(data => {
console.log('got the value ', data)
});
In my real project I want to implement queue where I will be sending pdf to the user. Like res.download(pdf path); but this function should be in pdf.on('completed',()=>{ res.download(pdf path); }); or in resolve(pdfPath) but I am not able to find anyway to send pdf to the user using queue because I don't know how to call response or resolve in other functions by using queue jobs.
Please help me. Thanks you

publisher subscribber pattern in nodejs sampel code is not woring with out setinterval

I am trying to implement pub/sub as shown below
publisher.js
var zmq = require('zmq');
var pub = zmq.socket('pub');
pub.bindSync('tcp://127.0.0.1:5555');
pub.send('pub msg');
/*
setInterval(function(){
console.log("sending message")
},500);*/
subscriber.js
var zmq = require('zmq');
var sub = zmq.socket('sub');
sub.connect('tcp://127.0.0.1:5555');
sub.subscribe(''); //herein lies the question
sub.on('message',function(msg){
console.log('Received msg:',msg);
})
the above subscriber will receive the message only when pub.send('pub msg'); is inside setInterval not sure about my understanding
I dont want use setInterval rather i have to send the message as soon it arrives
Please say how can i do it using pub/sub only i guess there is some basic understanding missing please help
in nodejs code i am trying using a route has
router.post('/putMsgIn0MQ', function (req, res, next) {
pushData(JSON.stringify(req.body))
})
var pushData = function(dataToPush) {
var zmqSocket = zmq.socket('pub')
var zmqPortPart = 'tcp://127.0.0.1:5555'
zmqSocket.bind(zmqPortPart);
zmqSocket.send(dataToPush);
}
subscriber
var sub = zmq.socket('sub');
sub.connect('tcp://127.0.0.1:5555');
sub.subscribe(''); //herein lies the question
console.log('Received msg:');
sub.on('message',function(msg){
console.log('Received msg:');
console.log(msg.toString())
var jsonPayload = msg.toString();
processData(jsonPayload, zmqPortObj.name);
})
Based on this code, from their documentaiton:
// pubber.js
var zmq = require('zmq')
, sock = zmq.socket('pub');
sock.bindSync('tcp://127.0.0.1:3000');
console.log('Publisher bound to port 3000');
setInterval(function(){
console.log('sending a multipart message envelope');
sock.send(['kitty cats', 'meow!']);
}, 500);
It seems that setInterval is just utility function :)
What is preventing that you fire it manually?
So for example, imaginary controller:
exports.animals= {
getCat: function (request, response) {
var animal = request.payload.catStuff;
//process the animal object
//and manually fire the message like:
sock.send(['kitty cats', 'meow!']);
...

Simple publishing of many messages to Rabbitmq

I want to send the same messages many times in a row, but i need to use a loop. When I use a loop though, no messages are sent. I am using amqp in Nodejs.
Here is the working code for sending a single messages. What should I do to send many. I have already tried just wrapping a while loop around the connection.publish part and nothing was sent.
var amqp = require('amqp');
var connection = amqp.createConnection({url: "amqp://tester:tstpsswrd#10.4.52.115:5672"});
connection.on('ready', function () {
connection.queue('my-queue', function (q) {
connection.publish('my-queue', 'hi');
});
});
I'm positive that I am doing something stupid wrong here, or maybe missing something. First time with rabbitmq.
Update, Loop example
var amqp = require('amqp');
var connection = amqp.createConnection({url: "amqp://tester:tstpsswrd#10.4.52.115:5672"});
connection.on('ready', function () {
connection.queue('my-queue', function (q) {
while(true){
connection.publish('my-queue', 'hi');
}
});
});
In practical scenario you can not and should not be having a infinite loop as such for writing to a message broker. There have to be some event based thing or a proper defined number.
Try this code you can use the for loop according to your requirement:
var amqp = require('amqp');
var connection = amqp.createConnection({ host: 'localhost', port: 5672});
connection.on('ready', function () {
for(var i=0; i<1000; i++){
var status = writeOnQueue("testing the queue"+i);
}
});
function writeOnQueue(xml){
var msg = xml;
console.log(msg);
try{
connection.exchange('test-exchange', {confirm: true},function(exchange) {
publish = exchange.publish('my-queue',msg, { mandatory: false });
console.log('sent the message success test-exchange');
return true;
});
}
catch(e){
console.log('Some error occured.'+ e);
}
}

Resources