express.js 4 and sockets with express router - express

I'm trying to create a really simple node API using express.js 4 but I need a few 'realtime' events for which I added socket.io. I'm fairly new to both so I'm likely missing something basic but I can't find good docs/tuts on this.
In the express app (created with the express generator) I have something like this based on simple examples and project docs that I read. This works OK and from client apps, I can send/receive the socket events:
var express = require('express');
var path = require('path');
var logger = require('morgan');
var api = require('./routes/api');
var app = express();
var io = require('socket.io').listen(app.listen(3000));
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/api', api);
io.sockets.on('connection', function (socket) {
console.log('client connect');
socket.on('echo', function (data) {
io.sockets.emit('message', data);
});
});
// error handlers omitted
module.exports = app;
but I want to use the sockets from my API routes (in the ./routes/api.js file that I 'require' above). For example, someone might use the API to PUT/POST a resource and I want that broadcast to connected socket.io clients.
I cannot see how to use the 'io' variable or organise the code currently in the io.sockets.on('connection' ... function inside express routes. Here's the ./routes/api.js file:
var express = require('express');
var router = express.Router();
var io = ???;
router.put('/foo', function(req, res) {
/*
do stuff to update the foo resource
...
*/
// now broadcast the updated foo..
io.sockets.emit('update', foo); // how?
});
module.exports = router;

One option is to pass it in to req object.
app.js:
var express = require('express');
var path = require('path');
var logger = require('morgan');
var api = require('./routes/api');
var app = express();
var io = require('socket.io').listen(app.listen(3000));
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
io.sockets.on('connection', function (socket) {
console.log('client connect');
socket.on('echo', function (data) {
io.sockets.emit('message', data);
});
});
// Make io accessible to our router
app.use(function(req,res,next){
req.io = io;
next();
});
app.use('/api', api);
// error handlers omitted
module.exports = app;
./routes/api.js:
var express = require('express');
var router = express.Router();
router.put('/foo', function(req, res) {
/*
do stuff to update the foo resource
...
*/
// now broadcast the updated foo..
req.io.sockets.emit('update', foo);
});
module.exports = router;

I've modified your files a little bit, may you check if it works?
You can pass the io you've defined to your routes like below;
require('./routes/api')(app,io);
I didn't test the Socket.IO parts but there is no syntax error and routes also working.
server.js file:
var express = require('express');
var app = express();
var path = require('path');
var logger = require('morgan');
var io = require('socket.io').listen(app.listen(3000));
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
io.sockets.on('connection', function (socket) {
console.log('client connect');
socket.on('echo', function (data) {
io.sockets.emit('message', data);
});
});
require('./routes/api')(app,io);
console.log("Server listening at port 3000");
api.js:
module.exports = function(app,io) {
app.put('/foo', function(req, res) {
/*
do stuff to update the foo resource
...
*/
// now broadcast the updated foo..
console.log("PUT OK!");
io.sockets.emit('update'); // how?
res.json({result: "update sent over IO"});
});
}

Supposing you want to access the SocketIO from anywhere in your application, not just in the router, you could create a singleton for it. This is what works for me:
//socket-singletion.js
var socket = require('socket.io');
var SocketSingleton = (function() {
this.io = null;
this.configure = function(server) {
this.io = socket(server);
}
return this;
})();
module.exports = SocketSingleton;
Then, you need to configure it using your server:
//server config file
var SocketSingleton = require('./socket-singleton');
var http = require('http');
var server = http.createServer(app);
SocketSingleton.configure(server); // <--here
server.listen('3000');
Finally, use it wherever you want:
//router/index.js
var express = require('express');
var router = express.Router();
var SocketSingleton = require('../socket-singleton');
/* GET home page. */
router.get('/', function(req, res, next) {
setTimeout(function(){
SocketSingleton.io.emit('news', {msg: 'success!'});
}, 3000);
res.render('index', { title: 'Express' });
});
module.exports = router;

One more option is to use req.app.
app.js
const express = require('express');
const path = require('path');
const logger = require('morgan');
const api = require('./routes/api');
const app = express();
const io = require('socket.io').listen(app.listen(3000));
// Keep the io instance
app.io = io;
app.use(logger('dev'));
app.use(express.static(path.join(__dirname, 'public')));
// ...
app.use('/api', api);
module.exports = app;
routes/api.js
const express = require('express');
const router = express.Router();
router.put('/foo', function(req, res) {
/*
* API
*/
// Broadcast the updated foo..
req.app.io.sockets.emit('update', foo);
});
module.exports = router;

Refactored Edudjr's answer.
Change the singleton to create a new instance of socket.io server
const { Server } = require('socket.io');
const singleton = (() => {
this.configure = (server) => this.io = new Server(server)
return this
})();
module.exports = singleton
Initialise your express app, the server and the singleton.
// initialise app
const app = express();
const server = require('http').createServer(app);
// configure socket.io
socket.configure(server)
Then in your router
const socket = require('/utils/socket-singleton');
socket.io.emit('event', {message: 'your message here'})

I think best way is to set io as a property of req, like below:
app.use(function(req,res,next){
req.io = io;
next();
});
app.use('/your-sub-link', your-router);

Related

express routes not working - returning 404 error

I'm trying to set up a simple express project, and backend will be purely for api and frontend will be vue. I tried adding an api router, and not only does it not work, but even the root page doesn't work. I've not coded in express for a very long time, and wondering if some one could guide me on what's the problem? The error message is 404 NotFoundError: Not Found.
I'm navigating to localhost:3000, and I can see the call on the terminal, so port should be correct too?
/src/api/urls.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
res.send('Just a test');
});
module.exports = router;
I didn't change the folder structure/naming for the original routes/index.js.
/src/routes/index.js
var express = require('express');
var router = express.Router();
router.get('/', function(req, res) {
res.send('Just a test');
});
module.exports = router;
And for the main file
/src/app.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var apiRouter = require('./api/urls');
var app = express();
app.use(require('connect-history-api-fallback')());
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/api', apiRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
All good except endpoint url that should be with middleware function only. Modifying these should work,
/src/api/urls.js
var express = require('express');
var router = express.Router();
router.get('/api/test', function(req, res) {
res.send('Just a test');
});
module.exports = router;
/src/routes/index.js
var express = require('express');
var router = express.Router();
router.get('/api/test2', function(req, res) {
res.send('Just a test');
});
module.exports = router;
In app.js you should register your routes as,
// Routes
app.use([
require('./src/api/urls'),
require('./src/routes/index'),
]);
Access by url ( http://<yourhost>/api/test1 )

Mean Stack Root Routing is not working

Can someone help me why default route is not working in my Mean App, But the next routing works
Here when I open http://localhost:3000 I am not able to see any output, But I have defined route in route.js which is working
var express = require('express');
var cors = require('cors');
var bodyparser = require('body-parser');
var mongoose = require('mongoose');
var path = require('path');
const port = 3000;
app.get('/', function (req, res) {
res.send('Test');
console.log('Opened the root path');
});
When I open the page with http://localhost:3000/main I am able to see the Output and also log written in the console
const express = require('express');
const router = express.Router();
router.get('/main', function (req, res, next) {
res.send('This is the Admin Landing Page');
});
router.get('/install', function (req, res, next) {
res.send('This is the Install Landing Page');
console.log('Opened the Install path');
});
module.exports = router;
It looks like you the code you pasted is the full version, and it's not runnable because:
You did not declare app variable.
You did not start the http server.
It's really hard to tell the root cause what's wrong of your code. Following codes works for me:
const express = require('express');
const port = 3000;
let app = express();
app.get('/', function (req, res) {
res.send('Test');
console.log('Opened the root path');
});
let server = require('http').createServer(app);
server.listen(port, function() {
console.log('Server started');
});

Express route function not showing text

app.js :
var index = require('./routes/index2');
var users = require('./routes/users');
app.use('/', index);
app.use('/users', users);
index2.js :
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
when I go to localhost:3000/ I don't see the text in send function
I guess your browser is you waiting for a response?
If you change index2.js to be a function and return the router, you'll be adding it as a middleware with app.use('url', middleware). I hope this helps
app.js
var index = require('./routes/index2');
var users = require('./routes/users');
app.use('/', index()); //Invoke the object to get the router back
app.use('/users', users);
index2.js
var express = require('express');
var router = express.Router();
module.exports = function() {
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
});
return router;
};
Returning the router from index2 to be used as a middleware

How to stop losing request parameters in expressjs?

I have defined my route in a separate route.js file as below (details removed for brevity)
var express = require('express');
var router = express.Router();
router.route('')put(function(req, res){
console.log(req.params.id); //prints undefined
});
and in server.js I map this route as below
var route = require('./routes/route.js');
app.use('/api/use/:id/role', route);
My route function is correctly invoked but the id parameter is not available in the router handler. Am I doing anything wrong?
Use the mergeParams option:
var router = express.Router({ mergeParams : true });
Full standalone example:
var express = require('express');
var app = express();
var server = app.listen(3000);
var router = express.Router({ mergeParams : true });
router.route('').get(function(req, res) {
console.log('id', req.params.id);
return res.sendStatus(200);
});
app.use('/api/use/:id/role', router);

Node express. Routes db.get and find method

Here is my routes/users.js file:
var express = require('express');
var router = express.Router();
/*
* GET userlist.
*/
router.get('/userlist', function(req, res) {
var db = req.db;
var collection = db.get('userlist');
collection.find({},{},function(e,docs){
res.json(docs);
});
});
module.exports = router;
In this code, what is the req object? And when we call req.db... that's made available to us via this right in app.js:
var mongo = require('mongodb');
var monk = require('monk');
var db = monk('localhost:27017/nodetest2');
var routes = require('./routes/index');
var users = require('./routes/users');
var app = express();
app.use(function(req,res,next){
req.db = db;
next();
});
What is the app.use exactly doing here?