22

I want to emit some data to the client when some API route gets called. I have to following code on server.js

var app  = express();
var http = require('http').Server(app);

var io   = require('socket.io')(http);

io.on('connection', function(socket){
  console.log('a user connected');

  socket.emit('tx', 'msg');

  socket.on('disconnect', function(){
    console.log('user disconnected');
  });
});

Now I have this route /test:

var bsg   = require('./routes/test');

And that file:

var express      = require('express');
var passport     = require('passport');
var router       = express.Router();

router.get('/test',  function(req, res) {
  //work here
});

module.exports = router;

On client side:

<script type="text/javascript">
   var socket = io();

   socket.on('tx', function(data) {
     console.log(data);
   });
</script>

Whats the best solution for this?

Thanks!

Express 4 / socket.io 1.4.5

mdv
  • 925
  • 3
  • 14
  • 28

2 Answers2

54

Attach the io instance to your app.

app.io = io;

Then you can access it via the request.

router.get('/test',  function(req, res) {
  req.app.io.emit('tx', {key:"value"});
});

I should note that the more "correct" way to attach data to express is using app.set('io', io) and app.get('io') to retrieve it, just in case express started using the io property for something.

If you are expecting to emit data to a single client you would need keep some kind of session mapping to link a standalone http request to a socket. A session ID might not be a one to one mapping though as you can have many sockets open for one session. You are better off handling a request/response pattern directly with socket.io callbacks.

Matt
  • 68,711
  • 7
  • 155
  • 158
  • No error on server but req.app.io.emit is not sending data to client :( – mdv Jun 01 '16 at 06:24
  • If I do this on /test route: req.app.io.on('connection', function(socket) { socket.emit('tx', 'some data'); }); client will receive data but ONLY on F5 (refresh) page, – mdv Jun 01 '16 at 06:36
  • Got it working, just had to do: req.app.io.emit('tx', data); – mdv Jun 01 '16 at 06:50
  • 9
    I personally prefer attaching the .io to the req as middleware with ... // Make io accessible to our router app.use(function(req, res, next) { 'use strict'; req.io = io; next(); }); So all I have to do is req.io.emit() , Which isn't any different, but it saves the .app – Alex J Jun 01 '16 at 13:47
  • Cant get it to work. Getting `TypeError: Cannot read property 'emit' of undefined`.What is app in this case? The express-instance right? – C4d Mar 23 '17 at 12:28
  • Yes, normally something like `const app = express()`. It will be the `app` object that server the request that is attached to `req` – Matt Mar 23 '17 at 21:31
  • @Matt I am in a similar boat - emit to a specific socket. Do you have any example of req/response pattern to socket Id? – Uma Maheshwaraa Jun 11 '17 at 19:20
  • my problem is after my queue is finished by promise. i wanted to emit to client so there is no req or res how to do it in that sceanrios – Rizwan Patel Jul 26 '17 at 17:19
  • I did like you said: app.get('/test', function(req, res) { req.app.io.emit('tx', {key:"value"}); res.render('socket.ejs', { message: req.app }); client side : }); – Tekraj Shrestha Apr 22 '19 at 15:26
  • @Matt I did like you said: on routes: app.get('/test', function(req, res) { req.app.io.emit('tx', {key:"value"}); res.render('socket.ejs', { message: req.app }); }); and on client side : window.socket.on('tx',function(data){ console.log(data); }); Url is: http://localhost:8099/test Now How can I connect it fro that url ? – Tekraj Shrestha Apr 22 '19 at 15:36
  • @ManishSingh I've found this pretty useful as the `app` instance is usually setup around the `io` instance. Otherwise you can pass a singleton around with the `io` instance but as the app routes expand it's a bit more work. – Matt May 20 '19 at 06:31
9

You can attach the instance to req object also:

app.use(function(req,res,next){
req.io = io;
next();
})

And to emit to a single client

req.io.to(<socketId of cient>).emit(data);
Amit Kumar
  • 407
  • 5
  • 7
  • is there a way to “emit to all except sender” using `req.io.to`? I know the way to normally do this is `socket.broadcast.emit` but I can’t find a way to access `socket` from within a route, only `io`. – user1063287 Nov 10 '18 at 17:52
  • I am also facing the same issue :( – Ronit Mukherjee Jun 18 '19 at 14:01
  • @user1063287 you are mixing http requests and socketio connections. You would have to write something that maps your http session manager to socketid's then exclude all the socketids coming from that http session. – Matt Jul 02 '19 at 05:36
  • @Matt kindly give an example of what you are saying. It will help us understand better. Thank you. – YulePale Aug 14 '19 at 16:49
  • @YulePale Ask that as a question with the detail of what you are attempting, mapping an express route to a socketio socket requires some context for how you do the mapping – Matt Aug 15 '19 at 00:15