usersam
usersam

Reputation: 1235

Node.js how to use socket.io in express route

In one of my node.js script i am trying to use socket.io in express route. I found many similar questions and tried to implement the solution as suggested but nothing worked out. May be because of my lack of understanding of express routes. I followed below links,

How use socket.io in express routes with node.js

Use socket.io in expressjs routes instead of in main server.js file

This is my app.js

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io').listen(server);

const PORT = 3000;
server.listen(PORT);
console.log('Server is running');
var api = require('./routes/api');

//app.use('/api', api);
app.use('/api', (req, res) => {
res.sendFile(__dirname + '/api.html');
});

app.get('/', (req, res) => {
   res.send("this is home location");
});

And route file api.js in ./routes folder

var express = require('express');
var router = express.Router();
var fs = require("fs");
var bodyParser = require('body-parser');

const app = express();
const server = require('http').createServer(app);
const io = require('socket.io').listen(server);

console.log("inside api route");

router.get('/', function(req, res, next) {
console.log("api route called");

const connections = [];
var jsonobj = [{name:"john",score:345},{name:"paul",score:678}]

io.sockets.on('connection',(socket) => {
    connections.push(socket);
    console.log(' %s sockets is connected', connections.length); // this is not printing

    socket.on('disconnect', () => {
       connections.splice(connections.indexOf(socket), 1);
    });

    socket.emit('server message', jsonobj);

 }); 
    //res.send(jsonobj) 
});

module.exports = router;

Socket.emit is not showing data on html page i am rendering on route use. My html code is,

//api.html

<!DOCTYPE html>
<html lang="en">

<body>
   <div class="container">
      <h1 class="jumbotron">
         Node js Socket io with  socket route example
      </h1>
      <div class="results">results</div>      
   </div>
   <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
   <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.0.4/socket.io.js"></script>
   <script>
    jQuery(document).ready(function() {
       var socket = io.connect();
       var jsondata = "";

       socket.on('server message', function(data){
         console.log('got data from server',data)
         jsondata = JSON.stringify(data);
         //console.log('jsondata',jsondata)
         $('.results').html(jsondata);
       });
    });   
 </script>
</body>
</html>

Please suggest what i am supposed to get route socket data in html page. Thanks

Upvotes: 10

Views: 16874

Answers (5)

abderrezague mohamed
abderrezague mohamed

Reputation: 211

localhost Basically, we create a simple middleware:

const { createServer } = require("http"); // you can use https as well
const express = require("express");
const socketIo = require("socket.io");

const app = express();
const server = createServer(app);
const io = socketIo(server, { cors: { origin: "*" } }); // you can change the cors to your own domain

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

// Now all routes & middleware will have access to req.io

app.use("/api", require("./routes/api")); // this file's express.Router() will have the req.io too.

server.listen(3000, () => console.log(`Server started!`));

This is a much better solution than passing the io to the required import as a functional parameter then utilized by the routes.

How would the api.js file utilize the io? See this example,

const express = require("express");

const Router = express.Router();

// Very simple example
Router.post("/new-message", (req, res) => {
  // You can do validation or database stuff before emiting
  req.io.emit("new-message", { content: req.body.content });
  return res.send({ success: true });
});

module.exports = Router;

You can manage multiple sockets, rooms, or even namespaces.

This solution is provided by: Aaryan

Upvotes: 0

Mezbaul
Mezbaul

Reputation: 1272

You tried to create and start the servers from two different places in your single project, which is inconvenient. You just need some cleanup, that's all.

app.js

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io').listen(server);


// Listen to sockets here instead of listening in routes/api.js
const connections = [];
var jsonobj = [{name:"john",score:345},{name:"paul",score:678}]

io.sockets.on('connection',(socket) => {
  connections.push(socket);
  console.log(' %s sockets is connected', connections.length); // this is not printing

  socket.on('disconnect', () => {
     connections.splice(connections.indexOf(socket), 1);
  });

  socket.emit('server message', jsonobj);

});

const PORT = 3000;
server.listen(PORT);
console.log('Server is running');
var api = require('./routes/api');

//app.use('/api', api);
app.use('/api', (req, res) => {
res.sendFile(__dirname + '/api.html');
});

app.get('/', (req, res) => {
   res.send("this is home location");
});

routes/api.js

var express = require('express');
var router = express.Router();
var fs = require("fs");
var bodyParser = require('body-parser');

// Comment these out
// const app = express();
// const server = require('http').createServer(app);
// const io = require('socket.io').listen(server);

console.log("inside api route");

router.get('/', function(req, res, next) {
console.log("api route called");

  // Comment these out

  // const connections = [];
  // var jsonobj = [{name:"john",score:345},{name:"paul",score:678}]

  // io.sockets.on('connection',(socket) => {
  //     connections.push(socket);
  //     console.log(' %s sockets is connected', connections.length); // this is not printing

  //     socket.on('disconnect', () => {
  //        connections.splice(connections.indexOf(socket), 1);
  //     });

  //     socket.emit('server message', jsonobj);

  //  }); 
      //res.send(jsonobj) 
});

module.exports = router;

Leave your api.html as it is. Hope this helps.

Upvotes: 0

WLGfx
WLGfx

Reputation: 1179

I am just starting to understand this myself, but I think where you are at is close.

In your app.js add to the end of the file:

const express = require('express');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io').listen(server);

const PORT = 3000;
server.listen(PORT);
console.log('Server is running');
var api = require('./routes/api');

//app.use('/api', api);
app.use('/api', (req, res) => {
res.sendFile(__dirname + '/api.html');
});

app.get('/', (req, res) => {
   res.send("this is home location");
});

app.set("socketio", io);    // <== this line

That stores the "io" variable in "socketio". Which you can grab in any of your other ".js" files.

var express = require('express');
var router = express.Router();
var fs = require("fs");
var bodyParser = require('body-parser');

const app = express();
const server = require('http').createServer(app);
//const io = require('socket.io').listen(server); // <== change this
const io = app.get("socketio"); // <== to this

console.log("inside api route");

router.get('/', function(req, res, next) {
console.log("api route called");

const connections = [];
var jsonobj = [{name:"john",score:345},{name:"paul",score:678}]

io.sockets.on('connection',(socket) => {
    connections.push(socket);
    console.log(' %s sockets is connected', connections.length); // this is not printing

    socket.on('disconnect', () => {
       connections.splice(connections.indexOf(socket), 1);
    });

    socket.emit('server message', jsonobj);

 }); 
    //res.send(jsonobj) 
});

module.exports = router;

And you should do that with any other variables which are required in other ".js" files.

Also note that in your files, you are setting the variables up again. It is better to do the same as I've shown you with "io". The only variable in other files I setup is "app" itself.

Hope this helps...

Upvotes: 0

Sohail
Sohail

Reputation: 4586

Ok, let's try to understand why do you need to send data via the socket inside a route in the first place. Websockets are meant for sending data asynchronously without the client having to make a request. If the client is already making an HTTP request, then you can just send the data in the HTTP response.

Now having said there, there are clearly some use cases where you have to send data to some WebSocket channel based on the actions of some OTHER user's requests. If that is the case, there are multiple ways of doing this. One clean way would be to use an event-driven architecture.

Try something like this... find my comments inline below -

const express = require('express');
const router = express.Router();
const fs = require("fs");
const bodyParser = require('body-parser');
const app = express();
const server = require('http').createServer(app);
const io = require('socket.io').listen(server);

// move the socket connection outside of the route controller
// you must register the event listeners before anything else
const connections = [];

io.sockets.on('connection', (socket) => {
    connections.push(socket);
    console.log(' %s sockets is connected', connections.length); // this is not printing

    socket.on('disconnect', () => {
        connections.splice(connections.indexOf(socket), 1);
    });
});


// Event emitter for sending and receving custom events
const EventEmitter = require('events').EventEmitter;
const myEmitter = new EventEmitter();

myEmitter.on('my-event', function (jsonobj) {
    // do something here like broadcasting data to everyone
    // or you can check the connection with some logic and 
    // only send to relevant user
    connections.forEach(function(socket) {
        socket.emit('server message', jsonobj);
    });
});

router.get('/some-route', function (req, res, next) {  
    const jsonobj = [{ name: "john", score: 345 }, { name: "paul", score: 678 }]

    // emit your custom event with custom data
    myEmitter.emit('my-event', jsonobj);

    // send the response to avoid connection timeout
    res.send({ok: true});
});

module.exports = router;

Upvotes: 8

alimcharaniya
alimcharaniya

Reputation: 147

At first glance, it looks like you are delcaring the URL prefix twice. Once in app.js and again in api.js.

Try localhost:port/api/api

If this is the case, change

router.get('/api', function(req, res, next){

to

router.get('/', function(req, res, next){

This will allow you to hit localhost:port/api and access your endpoint.

Upvotes: 5

Related Questions