Node.js: require() and passing variables - javascript

Started a project with npm which created a certain file structure:
www <-- require() calls app.js; instantiates server
app.js <-- instantiates var app = express(); and has module.exports = app;
Now, I'd like to use sockets.io. In my 'www' file, here is a code snippet:
var app = require('../app');
...
var server = http.createServer(app);
And I'd like to put all of my server-side socket listeners in app.js, but the following code:
var io = require('socket.io').listen(server);
requires server as an input. How do I make the server I instantiated in 'www' accessible in 'app.js'?

It seems a little strange. But if you insist on such a structure than you can export an object from www that will have app as it's property and a method that binds socket listeners an receives app object as a param.
module.exports = {
app: app,
bindSocketListeners: function(server, io) {
io.listen(server);
return io;
}
};
And call it:
var appObj = require('../app');
var io = require('socket.io');
var app = appObj.app;
var server = http.createServer(app);
io = appObj.bindSocketListeners(server, io)

Related

Can't access app.set variables?

I am creating a web app with expressjs at the moment.
I want to pass the port that the app is running on from the callingScript to the module:
callingScript.js
const http = require('http');
const app = require('./module');
const port = process.env.PORT || 3000;
app.set('port', port);
const server = http.createServer(app);
// it serves to the correct port, so the problem is passing it to module.js
app.listen(port);
module.js
const express = require('express');
const app = express();
// this always logs `undefined`
console.log(app.get('port'));
module.exports = app;
If I run the above application I want to see results like this:
$ node callingScript.js
3000
$ PORT=8080 bash -c 'node callingScript.js'
8080
but instead it prints undefined every time.
It looks like module.js isn't being passed the variable from its calling script.
How can I pass the port between the two?
You're logging at the wrong time. First your module.js runs, then your callingScript.js runs. The console.log with app.get is in your module.js, which runs before the app.set in callingScript.js.
If you want to do something with the configured port in code in module.js, just ensure you call that code after setting the port. For instance, here we export an object with both app and foo, where foo is a function:
const express = require('express');
const app = express();
module.exports = {
app,
foo: function() {
console.log("The port is " + app.get("port"));
}
};
then calling it:
const http = require('http');
const m = require('./module');
const port = process.env.PORT || 3000;
m.app.set('port', port);
m.foo(); // <====
const server = http.createServer(m.app);
// it serves to the correct port, so the problem is passing it to module.js
m.app.listen(port);
That said, it sounds like you might want to make your module expose a function that accepts a port and returns an app configured to use that port (along with anything else that needs to know what the port is).

Require (the same instance of) socket.io in multiple modules

I am a bit confused, about how to require and use modules in Node.js.
My scenario is the following:
I wrote a complete server in one single file, which uses Socket.io for realtime communication.
Now the index.js became pretty big, and I want to split the code into several modules to make it more managable.
For example I have some functions for serving a Survey to the clients, and getting back their answers. I put all those functions in a seperate module, and require it in the index.js. Works fine so far.
The only thing I am concerned about is, if there is another way to use the SAME socket instance inside the module.
My current coding looks like this:
index.js:
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var Survey = require('./survey');
io.on('connection', function (client) {
client.on('getCurrentQuestion', function (data) {
Survey.getCurrentQuestion(parseInt(data.survey_id), client.id);
});
});
server.listen(port, server_url, function () {
Survey.init(io);
});
survey.js:
var io = null;
var Survey = {};
Survey.init = function(socketio) {
io = socketio;
};
Survey.getCurrentQuestion = function(survey_id, socket_id) {
var response = {
status: "unknown",
survey_id: survey_id
};
// [...] some code that processes everything
// then uses Socket.io to push something back to the client
io.sockets.in(socket_id).emit('getCurrentQuestion', response);
};
module.exports = Survey;
This way it works, but I'm not happy passing io inside an init function to the required module.
What would be "the right way" to do this?
If I require('socket.io') inside the survey module, will it be the same instance as in index.js?
How would I even require that, since it needs the server, which needs the app, which is created in index.js?
I am confused and hope that somebody can help me. Thanks!
When you import a node.JS library you can also pass in objects. In your case the index.js file should be changed to the following:
//index.js
var express = require('express');
var app = express();
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var Survey = require('./survey')(io);
Then just change your survey.js code to take the io object:
//survey.js
module.exports = function (io) {
var Survey = {};
Survey.getCurrentQuestion = function(survey_id, socket_id) {
var response = {
status: "unknown",
survey_id: survey_id
};
// [...] some code that processes everything
// then uses Socket.io to push something back to the client
io.sockets.in(socket_id).emit('getCurrentQuestion', response);
};
return Survey;
};
To answer your other question:
If you require('socket.io') inside the survey module, it will be a different instance from index.js.
EDIT
If you want a more modern way of doing it...You could use ES6 format and create a class to do this better:
'ES6 index.js
import SurveyClass from './Survey';
import * as express from 'express';
let app = express();
let server = require('http').createServer(app);
let io = require('socket.io')(server);
let MySurveyClass= SurveyClass(io);
let myInstance = new MySurveyClass();
myInstance.getCurrentQuestion(5, "some-socket-id");
'ES6 survey.js
export default class Survey{
constructor(io){
this.io= io;
};
getCurrentQuestion(survey_id, socket_id) {
var response = {
status: "unknown",
survey_id: survey_id
};
// [...] some code that processes everything
// then uses Socket.io to push something back to the client
this.io.sockets.in(socket_id).emit('getCurrentQuestion', response);
};
}
When you do this in multiple modules:
var socketio = require('socket.io');
then it will be the same object for all requires.
But if you do this:
var io = require('socket.io')(server);
even if you have the same object in server in both places, io would be a different value because it would come from different invocations of the function returned by require('socket.io') even though those functions would be the same.
If you want to make sure that the io is the same then you'd have do e.g. something like this: Make a module that exports a promise of io and some way to initialize it - a function that gets the server and everything needed. Now, in the place where you have the server you can require the module and initialize it with that exported function, and in other places you can import the module and use a promise.
Instead of a promise you could use callbacks or even just a property on the exported object that could be initially undefined and gets defined when the socket.io is initialized but using a promise would probably be most simple solution to make sure that you're not using it while it's not ready yet.
You didn't mention in your question whether or not you use some framework and which one if any, but some of the frameworks like Hapi give you an easy way to share functionality like that. E.g. see:
https://hapijs.com/tutorials/plugins
If you use Hapi then you should use plugins. If other framework then search for a similar feature.

How do you set and retrieve an instance of BookshelfJS using Express?

I'm trying to use Bookshelf along with Express 4.0 and can't seem to get them working together or rather, I can't seem to follow "best practices". The Bookshelf docs mention that one should always reuse the same instance of it throughout the app. It even lists an example:
// When the app starts
var app = express();
var knex = require('knex')(dbConfig);
var bookshelf = require('bookshelf')(knex);
app.set('bookshelf', bookshelf);
// elsewhere, to use the bookshelf client:
var bookshelf = app.get('bookshelf');
var Post = bookshelf.Model.extend({
// ...
});
However, I can't seem to get it working when I have to use app.get() in a separate file. For example, here's my app.js file (the root of my entire app):
var express = require('express');
var app = express();
var db = require('./server/db/db');
app.set('bookshelf', db);
var api = require('./server');
app.use(api);
Here's my db.js file that gets required above:
var express = require('express');
var app = express();
var knex = require('knex')({ //my db settings go here });
var bookshelf = require('bookshelf')(knex);
module.exports = bookshelf;
The above code works if I require it directly. Here's where the issue turns up. Whenever I want to actually use the bookshelf connection, no matter what file I'm in, I follow the same process but it fails and "bookshelf" is always undefined. Here's an example of an index.js file that's required and called "api" in the app.js:
var express = require('express');
var app = express();
var db = app.get('bookshelf');
console.log(db);
DB always comes up as undefined. Whenever I try to make a new Model, I use the same process except I do an db.Model.extend({}) and trying to access the Model property throws an error (because it's undefined).
From what I can use both Bookshelf and Express docs agree that this should work and it doesn't. Any ideas?
This line creates a new app every time you call it:
var myApp = express();
If you want to set or get variables from the same app, you'll have to pass it as an argument.
var api = require('./server')(myApp);
And then in your api module:
module.exports = function(app){
var db = app.get('bookshelf');
//....
};
On a side note: you don't have to worry about singletons in Node.js all you have to do is just require it.
var db = require('./path/to/db/config');
It'll only be instantiated once and cached for later calls.

Using SocketIO in external lib for expressjs

my app.js is started with following code
var express = require("express"),
app = express(),
http = require('http'),
server = http.createServer(app),
io = require('socket.io').listen(server),
games = require("./lib/games");
Now I need to use socket.io in external lib which named games,how can I do that?
There are many ways to do this. One way is to have games export a function which accepts the socket.io object as a parameter.
For example:
// games.js
module.exports = function(io) { ... /* do something with io */ }
// app.js
var io = require('socket.io').listen(server),
games = require("./lib/games");
games(io);

node.js: socket.io vs express.static

I have the following server.js running:
module.exports = server;
var express = require('express');
var fs = require('fs');
var server = express.createServer();
var port = 58000;
server.listen(port);
var io = require('socket.io').listen(server);
server.use(express.static('/', __dirname + '/../public'));
server.use(express.logger());
io.on('connection', function(client){
console.log('new client connected ' + client);
client.on('message', function(){
console.log('client wants something');
});
});
Simple express.static server for files in a /public subfolder, plus socket.io functionality. With this setup, any request for the 'socket.io.js' file fails, i.e.
http://localhost:58000/socket.io/socket.io.js
returns a 404 error (file not found). Static file server works correctly. If I simply use the 'http' module instead of 'express' (commenting out express.static and express.logger lines) socket.io.js is served correctly. How can I combine both functionalities?
Express 3.0.0 (lastest) change its API.
Here is a question very similar to yours that delivers the response.
var express = require('express')
, http = require('http');
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server);
...
server.listen(8000);
Make sure you have the last versions of express.js and of socket.io.js.
My side it's working great with
express#2.5.8
socket.io#0.8.5
node#0.6.5
Otherwise, a solution can be to call var io = require('socket.io').listen(server); after your server.use

Categories