Socket.io with express backend - javascript

I want to implement socket.io on my express backend to inform the user about the task's state during the request (state is like started, processing, ...)
So, user clicks a button, socket connection opens and waits for events, during long running request backend emits data to client and finally response comes and request finishes, the result is shown to user.
in my index.js file, which I bootstrap the application
const express = require('express'),
app = express(),
router = require('./router'),
server = require('http').Server(app);
var io = require('socket.io')(server);
/* other required modules*/
server.listen(config.port,'127.0.0.1');
router(app, io);
in my router.js
const taskController = require('./controllers/task');
/* other required modules*/
module.exports = function(app, io) {
const apiRoutes = express.Router();
apiRoutes.post('/task/do', taskController.doTask(io));
}
in my task.js
exports.doAnalysis = function(socket){
return function(req, res){
socket.emit('task-start', 'Your task is started');
...
}
}
When I do this, it emits all the connected clients.
What I want to do is, emit from my controller's functions to only specific client which calls the endpoint so, sender only.
Also, I can't do in my task controller the following snippet:
exports.doTask = function(socket){
return function(req, res){
socket.on('connection', function(s){
s.emit('task-start', 'Your task is started');
...
})
}
}
Apparently, passing io object won't work for this scenario.
I know that I should get all socket ids and emit with specific socket id but,
what is the proper way to do this ?

Related

socket.io client not firing events on socket io server

I have built a backend and wanted to test it with some front-end code but for whatever reason, it is just not firing the events on the server. I dont get the "on connect" event triggered. I already downgrade the backend to a version similar to my testing script but still all the server shows, are logs like this.
[22/Oct/2021:14:06:21 +0000] "GET /socket.io/?EIO=4&transport=polling&t=NoeKlFT&b64=1 HTTP/1.1" 404 149 "-" "node-XMLHttpRequest"
I am using socket io for server and client version 3.1.2
Here is the backend code snippet
const dotenv = require("dotenv").config();
const express = require("express");
const app = express();
const server = require("http").createServer(app);
const io = require("socket.io")(server);
const morgan = require("morgan");
const cors = require("cors");
const config = require("./src/config/general");
const exchangeController = new ExchangeController(io); // I want to pass in the IO obejct to be able to deal with all the event stuff in another file besides the apps entry point file.
Here is the part where I want the IO object to be available and work with the events from there
class ExchangeController {
constructor(io) {
this.io = io;
this.exchangeService = new ExchangeService();
this.router = express.Router();
this.initRoutes();
this.io.on("connection", (socket) => {
console.log("incoming socket connection"); //gets never logged to console when connecting frontend
//do further stuff (deleted for post)
});
});
}
This is the frontend script for testing the connection
//client.js
const io = require('socket.io-client');
const socket = io("mysecreturl") //also tried localhost instead of deployed app url no difference
// Add a connect listener
socket.on('connect', function (socket) {
console.log('Connected!');
});
I really tried for way too long now without getting any results. I hope you guys can guide me on the right path again.
Cheers

How to set React app and API on same port?

I've got a React app that via an API pulls data from a separate database.
When I run it locally, the app is one port and the API is on another port.
Since when I make AJAX calls in the app to the API, I need to include the URL where the API can connect.
It works if I hardcode the separate port (e.g., the app is on http://localhost:3000 and the API on http://localhost:3100, making the AJAX url call to the API http://localhost:3100/api/trusts).
However, since the app and API are on different ports, I can't make the AJAX url a relative path because it erroneously sends the AJAX call to http://localhost:3000/api/trusts and not http://localhost:3100/api/trusts.
How do I get them to run on the same port?
Thanks!
Here's my server.js:
var express = require('express');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var path = require('path');
var app = express();
var router = express.Router();
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
//set our port to either a predetermined port number if you have set it up, or 3001
var port = process.env.PORT || 5656;
//db config
var mongoDB = 'mongodb://XXX:XXX!#XXX.mlab.com:XXX/XXX';
mongoose.connect(mongoDB);
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'MongoDB connection error:'));
//body parsing
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
// allow cross-browser
app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
next();
});
// handling static assets
app.use(express.static(path.join(__dirname, 'build')));
// api handling
var TrustsSchema = new Schema({
id: String,
name: String
});
var Trust = mongoose.model('Trust', TrustsSchema);
const trustRouter = express.Router();
trustRouter
.get('/', (req,res) => {
Trust.find(function(err, trusts) {
if (err) {
res.send(err);
}
res.json(trusts)
});
});
app.use('/api/trusts', trustRouter);
//now we can set the route path & initialize the API
router.get('/', function(req, res) {
res.json({ message: 'API Initialized!'});
});
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.listen(port, function() {
console.log(`api running on port ${port}`);
});
Below is the AJAX call I'm trying to make that doesn't work because the relative path is appended to the app's port (i.e., http://localhost:3000/) and not the API's port (i.e., http://localhost:3100/):
axios.get("/api/trusts")
.then(res => {
this.setState({trusts: res.data});
})
.catch(console.error);
To tell the development server to proxy any unknown requests to your API server in development, add a proxy field to your package.json, for example:
"proxy": "http://localhost:4000",
This way, when you fetch('/api/todos') in development, the development server will recognize that it’s not a static asset, and will proxy your request to http://localhost:4000/api/todos as a fallback. The development server will only attempt to send requests without text/html in its Accept header to the proxy.
"Keep in mind that proxy only has effect in development (with npm start), and it is up to you to ensure that URLs like /api/todos point to the right thing in production."
Note: this feature is available with react-scripts#0.2.3 and higher.
More details here: https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#proxying-api-requests-in-development

Emit to a particular client who started the request socket.io. Get socketid in router

I have the following app.js
var express = require('express');
var app = express();
// var responseHandlerRouter = require('./routes/responseHandlerRouter.js');
routes = require('./routes');
var server = app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
io = require('/usr/local/lib/node_modules/socket.io').listen(server);
app.use('/', routes(io));
routes.js
var express = require('express');
var router = express.Router();
module.exports = function (io) {
// all of this router's configurations
router.get('/login', function (req, res, next) {
io.emit('notification', 'news');
res.end('well finally I am here');
});
return router;
}
index.html
<!doctype html>
<html>
<head>
<script src="http://127.0.0.1:3000/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost:3000/login');
socket.on('notification', function (data) {
console.log(data);
});
</script>
</head>
<body>
<ul id='messages'></ul>
</body>
</html>
When I do a get on the URL, it emits the string 'news' to the browser, but its sent to all the clients.If i open up multiple browsers, its sent to all of them. How do I emit to a particular client? or how is it possible to get the socketid inside the router. Is it possible using the io object?
You may need to rethink your design here. Typically you'd get your reference to a particular client from the connect event
io.on('connection', function(socket) {
socket.emit('Hello World');
}
In your case you're passing in a reference to the entire io object. You may need to setup a way to handle the connection event and create a map (array in JS) between socket handles and client IDs. You can then reference the socket handle from the map by some sort of identifier coming from the req parameter inside your router.
Personally I've never used a router with socket.io and would recommend using namespaces or rooms.

How to redirect app's data stream to browser in real time?

My node app can read stream from kafka producer and console.log it to terminal in real time. But I would like to update it in my web app the same way (real time). How can I implement it?
I start my app with 'node index.js' command (or npm start).
index.js:
'use strict';
var express = require('express'),
app = express(),
server,
data = [];
...
consumer.on('message', function (message) {
console.log(message);
data.push(message);
//global.location.reload();
});
...
app.get('/', function(req, res){
res.send(data);
});
server = app.listen(3002, function(){
console.log('Listening on port 3002');
});
I think, that I need modify res.send(data) or add some code to on('message') event.
You should keep connection between client and server.
Try this socket.io package to update message in realtime.

Socket.io emit from Express controllers

I'm quite new to Node.js / Express, and I'm using it as a backend for an AngularJS app. I've looked all over StackOverflow for some help on my problem, but I can't seem to figure out how to port the suggestions to my code.
My application works as follows:
A long running Scala process periodically sends my Node.js application log messages. It does this by posting to an HTTP API
When the post is received, my application writes the log message to MongoDB
The log messages are then sent in real time to the Angular client.
I am having a problem with Node's modules, as I can't figure out how to refer to the socket instance in the Express controller.
As you can see, in server.js, socket.io is instantiated there. However, I would like the controller itself, logs.js, to be able to emit using the socket.io instance.
How can I refer to io in the controller? I'm not sure how to pass the io instance to the controller so I can emit messages?
Here is some of the Node code:
server.js
var app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server);
require('./lib/config/express')(app);
require('./lib/routes')(app);
server.listen(config.port, config.ip, function() {
console.log('Express server listening on %s:%d, in %s mode', config.ip, config.port, app.get('env'));
});
io.set('log level', 1); // reduce logging
io.sockets.on('connection', function(socket) {
console.log('socket connected');
socket.emit('message', {
message: 'You are connected to the backend through the socket!'
});
});
exports = module.exports = app;
routes.js
var logs = require('./controllers/logs'),
middleware = require('./middleware');
module.exports = function(app) {
app.route('/logs')
.post(logs.create);
}
logs.js
exports.create = function(req, res) {
// write body of api request to mongodb (this is fine)
// emit log message to angular with socket.io (how do i refer to the io instance in server.js)
};
You can use a pattern based on standard JS closures. The main export in logs.js will not be the controller function itself, but a factory function that will accept all needed dependencies, and create the controller:
exports.create = function(socket) {
return function(req, res) {
// write body of api request to mongodb
socket.emit();
}
}
Then, when you want to use it:
app.route('/logs').post(logs.create(socket));
Since you set up your routes in a separate package, you have to use the same pattern in routes.js - routes should receive the socket to use as a parameter.
This pattern works well if you want to handle those things with DI later, or test your controllers with mock "sockets".

Categories