Socket.io emit on Express Route
Asked Answered
D

3

23

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

Dragon answered 1/6, 2016 at 4:43 Comment(8)
#29335300Ticktock
im using module.exports = router; I can't do exports = function(app,io)Dragon
well unless you make it a global variable you have to pass the io instance for your route to use itTengdin
Don't know how to do that with my current setupDragon
Just updated route file to show my setupDragon
See the linked question attached, you could easily do that with var bsg = require('./routes/test')(app, io) and module.exports = function (app, io) { router.get(...); return router }Tengdin
I got: [TypeError: Router.use() requires middleware function but got a undefined]Dragon
@mdv, Your question is totally unclear to some peoples.Spiker
R
56

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.

Rash answered 1/6, 2016 at 6:15 Comment(11)
No error on server but req.app.io.emit is not sending data to client :(Dragon
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,Dragon
Got it working, just had to do: req.app.io.emit('tx', data);Dragon
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 .appInch
Cant get it to work. Getting TypeError: Cannot read property 'emit' of undefined.What is app in this case? The express-instance right?Abelabelard
Yes, normally something like const app = express(). It will be the app object that server the request that is attached to reqRash
@Rash I am in a similar boat - emit to a specific socket. Do you have any example of req/response pattern to socket Id?Follett
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 sceanriosCyclops
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 : });Interrex
@Rash 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: localhost:8099/test Now How can I connect it fro that url ?Interrex
@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.Rash
C
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);
Circassia answered 30/5, 2017 at 5:59 Comment(5)
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.Couplet
I am also facing the same issue :(Stepheniestephens
@Couplet 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.Rash
@Rash kindly give an example of what you are saying. It will help us understand better. Thank you.Fuentes
@Fuentes 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 mappingRash
S
0

my code of app.js file is:

import express from 'express';
import morgan from 'morgan';
import cors from 'cors';
import path from 'path';
import mongoose from 'mongoose';
import bodyParser from 'body-parser';

import { Server } from 'http';
import { Server as SocketIOServer } from 'socket.io';

const uri='mongodb://localhost:27017/db_pruebas';

const options={
                useNewUrlParser:true, 
                useCreateIndex:true,
                useUnifiedTopology: true,
                useFindAndModify: false
             };

mongoose.Promise=global.Promise; 
mongoose.connect(uri,options).then(
    ()=>{ console.log('conectado a mongo: db_checklist'); },
    err=>{console.log('error conectando con mongo:'+err);}

);


const app = express();
app.use(morgan('tiny'));
app.use(cors());
app.use(express.json());


app.use(express.urlencoded({extended:true}));
app.use(bodyParser.json({limit: '50mb'}));



const server = new Server(app);
const io = new SocketIOServer(server, {
    cors: {
        origin: "*",
    }
});

app.io = io;

app.use('/api',require('./routes/clientes'));
app.use('/api',require('./routes/sucursales'));
app.use('/api',require('./routes/compras'));
app.use('/api',require('./routes/usuarios'));
app.use('/api',require('./routes/login'));


const history=require('connect-history-api-fallback');
app.use(history());
app.use(express.static(path.join(__dirname,'public')));


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

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


const PORT = process.env.PORT || 3000;
server.listen(PORT, () => {
  console.log(`Server listening on port: ${PORT}`);
});

in code of one example of route use io is:

import express from 'express';
const router=express.Router();
import mongoose from 'mongoose';

import Cliente from '../models/Clientes';
import Notificacion from '../models/Notificaciones';




router.post('/nuevo-cliente', async(req, res)=>{
  
     try{

        const body=req.body;

        console.log(body);
      
        const Cliente=await Vehiculo.create(body);

  
       if(Cliente){

        let bodynofificacion={
            'texto':'Nuevo cliente:'+ body.nombre+ ' a las:'+body.fecha,
            'activo':1
        };

        const notificacionDB=await Notificacion.create(bodynofificacion);
         req.app.io.emit('new-notification', notificacionDB);

        res.status(200).json({
            'success':true,
            'msg':"chekclist ingresado correctamente",
        });

       }

    
   
   
     }catch(error){
        console.log(error)
        return res.status(500).json({
            mensaje:'Ocurrio un error',
            error
        })
     }
   
   });




        module.exports=router;
Stocktonontees answered 30/7, 2024 at 22:24 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.