Modularizing Socket.io with Express 4 - javascript

I'm trying to modularize my application files and I'm having problems with Socket.io. I would like to use the io inside my routes.js. Something like this:
var router = require('express').Router();
var io = require('./sockets/my-io');
router.get('/', function(req, res) {
io.emit('request-detected');
});
module.exports = router;
But I can't do, because the socket.io needs the app server, and when I'm inside the routes.js file, the app server is not listening or being exported yet.
Can you give me a solution, or any other approach to this problem?
Here's what I have, and if it's possible, I would like to keep the file structure:
app.js
var app = require('express')();
var routes = require('./routes');
/* ... */
app.use('/contacts', routes);
module.exports = app;
bin/www
#!/usr/bin/env node
var app = require('../wallet');
var server = app.listen(port, function() {
debug('Express is listening o port ' + port);
});
routes.js
var router = require('express').Router();
router.get('/', function(req, res) {
console.log('hey');
});
module.exports = router;

You can do it by passing the io variable to your routes module.
bin/www
#!/usr/bin/env node
var app = require('./app');
var server = app.listen(3000, function() {
console.log('Express is listening on port 3000');
}); // start the server
var socket = require('./socket')(server); // require socket.io code
var routes = require('./routes')(socket); // require routes
app.use('/', routes);
app.js
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
app.set('views engine', 'ejs');
app.set('views', __dirname + '/');
module.exports = app;
socket.js
var socketio = require('socket.io');
function init(server) {
var io = socketio(server);
io.on('connection', function (socket) {
console.log("socket connected");
socket.on('newEvent', function (data) {
console.log(data);
});
});
return io;
}
module.exports = init;
routes.js
var express = require('express');
var route = express.Router();
function init(io) {
route.get('/', function (req, res) {
res.render('index.ejs', {});
setTimeout(function() {io.emit('newEvent', {message: "Hi from the server"})}, 2000);
});
return route;
}
module.exports = init;
The code above worked for me. However, I'm not sure why you want to do that.
Inside the router, you still have full control of what you want to send to the user via html, so you can just add the data to the html directly.
The idea of socket.io is that you can send data between the client and back once he has loaded the html and established a connection to your server with socket.io.
As you can see in the routes.js, I had to add a timeout to the emit. This is because the socket event will be emit before the browser has reloaded the page. In my case the browser logged the event and then immediately refreshed, losing the data you just sent.
Another problem is that you don't know anything about the socket of the client that is requesting the page because he hasn't connected yet. This means that calling io.emit() will send the event to all connected sockets.
As I said, this really depends on what exactly you want to do.
EDIT:
Instead of updating your contacts using ajax, you can do that with socket.io.
socket.js
var socketio = require('socket.io');
function init(server) {
var io = socketio(server);
io.on('connection', function (socket) {
console.log("socket connected");
socket.on('newContact', function (data, callback) {
// add data.contactName to db
// after adding something, you use the callback to
// send the added data back to the client
// callback(newContact);
});
});
return io;
}
module.exports = init;
index.html
<script type="text/javascript" >
var socket = io();
// call this emit when the user wants to add a contact
socket.emit('newContact', {contactName: name}, function(newContact) {
// here you will get the result from the server and you can
// update the html with jquery for example
});
</script>

If i understand your question correctly ,maybe you can try this way.
in your routes.js file
var app = require('./app');
var server = require('http').createServer(app);
var io = require('./sockets/my-io')(server);
var route = app.Router();
in your app.js file
var port = process.env.PORT || 3000;
app.listen(port,function(){
console.log('server on port ' + port)
})

Related

How to call a socket.on() from a separated file

I'm trying to call a socket.on() event from an external .js file and I can't figure out what I'm missing...
I'm using NodeJS with ExpressJS.Below are the files:
app.js(the server file)
const fs = require('fs');
const express = require('express');
const app = express();
const http = require('http').Server(app);
var io = require('socket.io')(http);
....
//Socket Io functions
const ioObj = require( './library/io.js')(app, express, io);
// This route will be used to print the type of HTTP request the particular Route is referring to
router.use(function (req, res, next) {
console.log("/" + req.method);
next();
});
....
/library/io.js (sockets file)
module.exports = function(app, express, io){
io.on('connection', async function(socket) {
socket.on('refreshPage', function(){
console.log("page should now be refreshed !!");
socket.emit("refreshPageNow");
});
....
});
}
What I'm trying to do is to call/access the refreshPage event from /library/io.js so I can send further a "refresh webpage" signal.
I tried to do something like :
io.sockets.emit("refreshPage");
and
ioObj.sockets.emit("refreshPage");
But didn't work...

Triggering socket.io real-time notifications from Express 4 middleware

I am building a real-time notification system using socket.io. This is my server-side code at the moment:
bin/www:
var app = require('../app');
var server = http.createServer(app);
var io = app.io
io.attach(server);
server.listen(port, function(err) {
if (err) console.log(err);
console.log('Listening on port ' + port + '...');
});
app.js:
var socket_io = require('socket.io');
var express = require('express');
var app = express();
var io = socket_io();
app.io = io;
require('./config/socket')(app.io);
config/socket.js:
var User = require('../controllers/user');
module.exports = function (io) {
io.on('connection', function (socket) {
console.log('Socket.io connected');
socket.emit('connection', "Connection created.");
socket.on('send notification', function(data) {
User.createNotification(socket.request.user, data);
});
});
};
routes/index.js:
var express = require('express');
var User = require('../controllers/user');
var router = express.Router();
router.post('/order', User.order);
module.exports = router;
controllers/user.js:
var User = require('../models/user').model;
var io = require('socket.io');
module.exports = {
order: function(req, res) {
/* some create order code */
io.emit('send notification', 'Your order was successful!');
res.sendStatus(200);
}
}
I keep getting the error TypeError: io.emit is not a function whenever I try to call the route POST /send even though I am clearly initiating socket.io in my app.js and bin/www files and requiring it in controllers/user.js. All the examples I've seen online emit notifications from within this part:
io.on('connection', function (socket) {
socket.emit(event, msg);
});
but I want my notifications to be triggered from the middleware so I can send custom notifications to the user when certain events happen in the application backend.
Try the following instead:
io.on('connection', function(socket){
socket.on('xxx', function(obj){
io.emit('xxx', {xxx: xxx})
})
})
This should suppress your TypeError:.

How to use Socket.io combined with Express.JS (using Express application generator)

I'm trying to use Socket.io combined with Express.JS (using Express application generator).
I've found some aswers how to do this (Using socket.io in Express 4 and express-generator's /bin/www).My problem is that i cannot make use of the sockets inside the routes folder.
I can use them in the app.js and bin/www.js files. When i call the route index.js it just keeps loading the webpage for a long time without giving any errors.
bin/www.js
...
/**
* Create HTTP server.
*/
var server = http.createServer(app);
var io = app.io
io.attach( server );
...
app.js
...
// Express
var app = express();
// Socket.io
var io = socket_io();
app.io = io;
var routes = require('./routes/index')(io);
...
routes/index.js
module.exports = function(io) {
var app = require('express');
var router = app.Router();
io.on('connection', function(socket) {
console.log('User connected');
});
return router;
}
Here is a simple example on how to use Socket.io with Express that I made available on GitHub here:
https://github.com/rsp/node-websocket-vs-socket.io
The backend code is this:
var path = require('path');
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', (req, res) => {
console.error('express connection');
res.sendFile(path.join(__dirname, 'si.html'));
});
io.on('connection', s => {
console.error('socket.io connection');
for (var t = 0; t < 3; t++)
setTimeout(() => s.emit('message', 'message from server'), 1000*t);
});
http.listen(3002, () => console.error('listening on http://localhost:3002/'));
console.error('socket.io example');
See https://github.com/rsp/node-websocket-vs-socket.io/blob/master/si.js
As you can see here, I am creating the express app with:
var app = require('express')();
Then I create an http server with that app with:
var http = require('http').Server(app);
And finally I use that http server to create the Socket.io instance:
var io = require('socket.io')(http);
After running:
http.listen(3002, () => console.error('listening on http://localhost:3002/'));
it all works together.
You can see the entire example on GitHub with both backend and frontend code that works. It currently uses Express 4.14.0 and socket.io 1.4.8.
For anyone who still want to use socket.io and express http request. Easiest way is to create two seprate instance of http server listing to different ports. 1 for websockets and 2nd for api requests.
const express = require("express");
const app = express();
const httpServer = require("http").createServer(app);
const io = require("socket.io")(httpServer, {
path: '/'
});
// routes and io on connection
httpServer.listen(5000, () => {
console.log("Websocket started at port ", 5000)
});
app.listen(3000, () =>{
console.log("Http server listening at", 3000)
})

What is happening when require("http").Server() is evaluated with an Express app as its argument?

I was reading the Socket.io Chat Demo here: http://socket.io/get-started/chat/
and I got confused when looking at their require statements.
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.get('/', function(req, res){
res.sendfile('index.html');
});
io.on('connection', function(socket){
console.log('a user connected');
});
http.listen(3000, function(){
console.log('listening on *:3000');
});
Am I correct in thinking that require("express") produces an executable Express function (with all of the necessary functions and fields that go with that), and that require("http").Server(app) creates a http.Server object with all of its fields and functions.
If so, I am confused because Express creates a server when we call the .listen function so it seems redundant and backwards to pass an Express app to an http module server.
So, my question is, what is really happening here?
The http server expects a function which has the following signature:
function(req, res)
require('express')(); will create a function with that signature which handles all the magic that express makes available like routing, middleware, etc. Express can create it's own http server instance, but since you're also using socket.io (which expects access to the http server as well), you'll need a separate http instance.
var app = require('express')(); //infact, app is a requestListener function.
var http = require('http').Server(app); // infact, function Server eqs http.createServer;
// infact,app.listen eqs http.listen
other code from nodejs and express model
we can see, require("express")() return app is a function.
//express/lib/express.js https://github.com/strongloop/express/blob/master/lib/express.js
var proto = require('./application');
exports = module.exports = createApplication;
function createApplication() {
var app = function(req, res, next) {
app.handle(req, res, next);
};
mixin(app, EventEmitter.prototype, false);
mixin(app, proto, false);
app.request = { __proto__: req, app: app };
app.response = { __proto__: res, app: app };
app.init();
return app;
}
we can see, node.createServer eqs Server
//nodejs/lib/http.js https://github.com/nodejs/node/blob/master/lib/http.js
exports.createServer = function(requestListener) {
return new Server(requestListener);
};
we can see, express app.listen eqs http.listen
//express/lib/application.js https://github.com/strongloop/express/blob/master/lib/application.js
app.listen = function listen() {
var server = http.createServer(this);
return server.listen.apply(server, arguments);
};
//nodejs/lib/_http_server.js https://github.com/nodejs/node/blob/master/lib/_http_server.js
function Server(requestListener) {
if (!(this instanceof Server)) return new Server(requestListener);
net.Server.call(this, { allowHalfOpen: true });
if (requestListener) {
this.addListener('request', requestListener);
}
this.httpAllowHalfOpen = false;
this.addListener('connection', connectionListener);
this.addListener('clientError', function(err, conn) {
conn.destroy(err);
});
const app = express();
let server = require('http').Server(app);
server.listen(3000);
This makes the server listening to the port we provide.
const app = express();
let server = require('http').createServer(app);
server.listen(3000);
So Server and createServer are the same according to the node.js HTTP library source code.
Ref : nodejs/lib/http.js
exports.Server = Server;
exports.createServer = function(requestListener) {
return new Server(requestListener);
};

Cache resource on startup of express

I am wondering if someone could help me point out how to cache a resource in express/node on startup of the web server. The myCol.find is very expensive, so I would like to just run it once on startup, and cache the result for all subsequent requests. Is there a startup step I can tie into? Can this be done synchronously before the server starts accepting requests?
I have the code below, but would like to reference a cached variable instead of the mongo db
var express = require('express');
var app = express();
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test');
var myCol = require('./customModule');
var port = process.env.PORT || 8080;
var router = express.Router();
router.get('/test/:testId', function(req, res) {
myCol.find(function(err, allResults) {
res.json(allResults);
});
});
app.use('/api', router);
app.listen(port);
Yep, you just need to make sure your server starts listening after you've fetched your results through myCol.find():
var express = require('express');
var app = express();
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test');
var myCol = require('./customModule');
var port = process.env.PORT || 8080;
var router = express.Router();
var myResultsCache;
router.get('/test/:testId', function(req, res) {
res.send(myResultsCache);
});
app.use('/api', router);
myCol.find(function(err, allResults) {
//you should add some error handling here
myResultsCache = allResults;
app.listen(port);
});
You could swap the order of myCol.find and the route handler, so that the relevant code would be:
myCol.find(function(error, results) {
if (error) throw error;
router.get('/test/:testId', function (request, response) {
response.json(results);
});
});
This is still asynchronous, but the server would only be able to respond after the find operation has been done.

Categories

Resources