I'm quite new to Node.js / Express, and I'm using it as a backend for an AngularJS app. I've looked all over StackOverflow for some help on my problem, but I can't seem to figure out how to port the suggestions to my code.
My application works as follows:
A long running Scala process periodically sends my Node.js application log messages. It does this by posting to an HTTP API
When the post is received, my application writes the log message to MongoDB
The log messages are then sent in real time to the Angular client.
I am having a problem with Node's modules, as I can't figure out how to refer to the socket instance in the Express controller.
As you can see, in server.js, socket.io is instantiated there. However, I would like the controller itself, logs.js, to be able to emit using the socket.io instance.
How can I refer to io in the controller? I'm not sure how to pass the io instance to the controller so I can emit messages?
Here is some of the Node code:
server.js
var app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server);
require('./lib/config/express')(app);
require('./lib/routes')(app);
server.listen(config.port, config.ip, function() {
console.log('Express server listening on %s:%d, in %s mode', config.ip, config.port, app.get('env'));
});
io.set('log level', 1); // reduce logging
io.sockets.on('connection', function(socket) {
console.log('socket connected');
socket.emit('message', {
message: 'You are connected to the backend through the socket!'
});
});
exports = module.exports = app;
routes.js
var logs = require('./controllers/logs'),
middleware = require('./middleware');
module.exports = function(app) {
app.route('/logs')
.post(logs.create);
}
logs.js
exports.create = function(req, res) {
// write body of api request to mongodb (this is fine)
// emit log message to angular with socket.io (how do i refer to the io instance in server.js)
};
You can use a pattern based on standard JS closures. The main export in logs.js will not be the controller function itself, but a factory function that will accept all needed dependencies, and create the controller:
exports.create = function(socket) {
return function(req, res) {
// write body of api request to mongodb
socket.emit();
}
}
Then, when you want to use it:
app.route('/logs').post(logs.create(socket));
Since you set up your routes in a separate package, you have to use the same pattern in routes.js - routes should receive the socket to use as a parameter.
This pattern works well if you want to handle those things with DI later, or test your controllers with mock "sockets".
Related
I want to implement socket.io on my express backend to inform the user about the task's state during the request (state is like started, processing, ...)
So, user clicks a button, socket connection opens and waits for events, during long running request backend emits data to client and finally response comes and request finishes, the result is shown to user.
in my index.js file, which I bootstrap the application
const express = require('express'),
app = express(),
router = require('./router'),
server = require('http').Server(app);
var io = require('socket.io')(server);
/* other required modules*/
server.listen(config.port,'127.0.0.1');
router(app, io);
in my router.js
const taskController = require('./controllers/task');
/* other required modules*/
module.exports = function(app, io) {
const apiRoutes = express.Router();
apiRoutes.post('/task/do', taskController.doTask(io));
}
in my task.js
exports.doAnalysis = function(socket){
return function(req, res){
socket.emit('task-start', 'Your task is started');
...
}
}
When I do this, it emits all the connected clients.
What I want to do is, emit from my controller's functions to only specific client which calls the endpoint so, sender only.
Also, I can't do in my task controller the following snippet:
exports.doTask = function(socket){
return function(req, res){
socket.on('connection', function(s){
s.emit('task-start', 'Your task is started');
...
})
}
}
Apparently, passing io object won't work for this scenario.
I know that I should get all socket ids and emit with specific socket id but,
what is the proper way to do this ?
I have the following app.js
var express = require('express');
var app = express();
// var responseHandlerRouter = require('./routes/responseHandlerRouter.js');
routes = require('./routes');
var server = app.listen(3000, function () {
console.log('Example app listening on port 3000!');
});
io = require('/usr/local/lib/node_modules/socket.io').listen(server);
app.use('/', routes(io));
routes.js
var express = require('express');
var router = express.Router();
module.exports = function (io) {
// all of this router's configurations
router.get('/login', function (req, res, next) {
io.emit('notification', 'news');
res.end('well finally I am here');
});
return router;
}
index.html
<!doctype html>
<html>
<head>
<script src="http://127.0.0.1:3000/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://localhost:3000/login');
socket.on('notification', function (data) {
console.log(data);
});
</script>
</head>
<body>
<ul id='messages'></ul>
</body>
</html>
When I do a get on the URL, it emits the string 'news' to the browser, but its sent to all the clients.If i open up multiple browsers, its sent to all of them. How do I emit to a particular client? or how is it possible to get the socketid inside the router. Is it possible using the io object?
You may need to rethink your design here. Typically you'd get your reference to a particular client from the connect event
io.on('connection', function(socket) {
socket.emit('Hello World');
}
In your case you're passing in a reference to the entire io object. You may need to setup a way to handle the connection event and create a map (array in JS) between socket handles and client IDs. You can then reference the socket handle from the map by some sort of identifier coming from the req parameter inside your router.
Personally I've never used a router with socket.io and would recommend using namespaces or rooms.
My node app can read stream from kafka producer and console.log it to terminal in real time. But I would like to update it in my web app the same way (real time). How can I implement it?
I start my app with 'node index.js' command (or npm start).
index.js:
'use strict';
var express = require('express'),
app = express(),
server,
data = [];
...
consumer.on('message', function (message) {
console.log(message);
data.push(message);
//global.location.reload();
});
...
app.get('/', function(req, res){
res.send(data);
});
server = app.listen(3002, function(){
console.log('Listening on port 3002');
});
I think, that I need modify res.send(data) or add some code to on('message') event.
You should keep connection between client and server.
Try this socket.io package to update message in realtime.
I need to create an application that proxies a request from port A to Port B.
For instance if a user connects on port 3000 he will be routed (under the hood) to port 3001, therefore the "original" application will run on port 3001 but in the client (browser) the user will put port 3000.
Not redirect...
http://example.com:3000/foo/bar
A new server will be created which listens to port 3001 and all the call are actually to port 3000 running with the new server and new port.
Since port 3000 is actually occupied,by my reverse proxy app? how should I test it...
Is there a way to test this to verify that this is working,e.g. by unit testing?
I've found this module https://github.com/nodejitsu/node-http-proxy which might be able to help.
Straight from the node-http-proxy docs, this is quite simple. You can test it simply by making an HTTP request to port 3000 -- if you get the same response as you do on port 3001, it's working:
var http = require('http'),
httpProxy = require('http-proxy');
//
// Create a proxy server with custom application logic
//
var proxy = httpProxy.createProxyServer({});
var server = http.createServer(function(req, res) {
// You can define here your custom logic to handle the request
// and then proxy the request.
proxy.web(req, res, {
// Your real Node app
target: 'http://127.0.0.1:3001'
});
});
console.log("proxy listening on port 3000")
server.listen(3000);
I highly recommend you write a suite of integration tests using some thing like mocha for your project as well - in this way, you can run your tests both against your server directly and against your proxy. If tests pass against both, then you can be assured your proxy is behaving as expected.
A unit test using mocha and should.js would look something like this:
var should = require('should');
describe('server', function() {
it('should respond', function(done) {
// ^ optional synchronous callback
request.get({
url: "http://locahost:3000"
// ^ Port of your proxy
}, function(e, r, body) {
if (e)
throw new Error(e);
body.result.should.equal("It works!");
done(); // call the optional synchronous callback
});
});
});
You then simply run your test (once Mocha is installed):
$ mocha path/to/your/test.js
You can either verify that this is working by adding the following
to the proxy request (as described in Remus answer )
proxy.on('proxyReq', function (proxyReq, req, res, options) {
res.setHeader('App-Proxy', 'proxy');
});
In this way you can verify that your "original" call is working against the new server proxy and even provide the ability to create a UT,in addition you can use the changeOrigin:true property...
This was working a few months ago when I was creating an HTTPS server, but I switched to http (not sure this switch is directly related, just mentioning it in case) today when revisiting this application, where I create a server and pass it to socket.io:
init.js
var server = require(dirPath + "/custom_modules/server").serve(80);
var socket = require(dirPath + "/custom_modules/socket").socket(server);
It is important that I pass the server to socket.io (I know there are alternate ways of initializing the socket) this way because that's how it has to be done in order to encrypt the websocket connection when I switch back to serving HTTPS later.
So my server module:
//serve files
module.exports.serve = function(port) {
//var server = https.createServer(options, function(req, res) { // SSL Disabled
var server = http.createServer(function(req, res) {
// Parse & process URL
var reqInfo = url.parse(req.url, true, true), path = reqInfo.pathname;
// Quickly handle preloaded requests
if (preloaded[path])
preloadReqHandler(req, res, preloaded[path], path);
// Handle general requests
else
generalReqHandler(req, res, reqInfo);
}).listen(port);
return server; //this should be returning an http server object for socket.io
};
and my socket module:
module.exports.socket = function(server) {
//create socket
var socket = require(dirPath + '/node_modules/socket.io')(server);
// ^ error
// .. snip ..
//handle client connection
socket.on("connection", function(client) {
// .. snip ..
});
};
and my error:
/home/ec2-user/Sales_Freak/server/custom_modules/socket.js:17
var socket = require(dirPath + '/node_modules/socket.io')(server);
^
TypeError: object is not a function
at Object.module.exports.socket (/home/ec2-user/Sales_Freak/server/custom_modules/socket.js:17:59)
at Object.<anonymous> (/home/ec2-user/Sales_Freak/server/init.js:16:59)
Assume all of the necessary Node.JS modules are required properly above. What silly mistake am I making today?
The exported module is not a function, refer to your previous statement:
var socket = require(dirPath + "/custom_modules/socket").socket(server);
And compare that to your current statement:
var socket = require(dirPath + '/node_modules/socket.io')(server);
I think you meant to do this instead.
var socket = require(dirPath + '/node_modules/socket.io').socket(server);
This might or might not be helpful to others, but my problem was that I changed the directory of my Node.js server files and socket.io wasn't installed in the new location.
The module was there in node_modules but not installed. I'm actually not sure how installation works with npm modules, but the module existed and therefore didnt throw an error saying it didnt exist, but did not act like it was really there until I did npm install socket.io
If you get this error in this situation, you forgot install socket.io.