Best practices in structuring Node.js & Socket.io app? - node.js

Currently I'm developing a node server which uses websockets for communication.
I'm used to applying MVC (or MC) pattern in my apps. I'd like to structure my socket.io in similiar way and my question is how to do this in the best way?
For now I have something like this:
utils/socket.ts:
type IO = null | SocketIO.Server;
let io: IO;
export function init(ioServer: IO) {
io = ioServer;
}
export function getIO(): IO {
return io;
}
app.ts:
import express from 'express';
...
import { init } from './utils/socket';
import startSockets from './controllers';
const app = express();
...
const server = app.listen(process.env.PORT || 5000);
const io = socketio.listen(server);
if (io) {
init(io);
io.on('connect', startSockets);
}
controllers/index.ts:
import { onConnect } from './connect';
import { getIO } from '../utils/socket';
export default function (socket: SocketIO.Socket) {
const io = getIO();
socket.on('connect-player', onConnect);
}
controllers/connect.ts:
import Player from '../models/Player';
export const onConnect = async function (this: SocketIO.Socket, name: string) {
const player = await Player.create({ name });
this.broadcast.emit('player-connected', `Player ${name} joined the game!`);
this.emit(
'player-connected',
`Congratulations ${name}, you successfully joined our game!`,
player,
);
this.on('disconnect', onDisconnect.bind(this, player.id));
};
const onDisconnect = async function (this: SocketIO.Socket, playerId: string) {
const player = await Player.findById(playerId);
await player?.remove();
this.broadcast.emit(
'player-connected',
`Player ${player?.name} left our game!`,
);
};
I'm not sure about using 'this' in my controller and about that singleton pattern in utils/socket.ts. Is that proper? The most important for me is to keep the app clear and extensible.
I'm using express and typescript.
Player.ts is my moongoose schema of player.

I had been struggling with socket.io + express.js a while back, specially I am as well use to apply MVC patterns.
While searching, googling and stack overflowing, these links helped me with the project back than.
https://blueanana.github.io/2017/03/18/Socket-io-Express-4/
https://github.com/onedesign/express-socketio-tutorial
https://gist.github.com/laterbreh/5d7bdf03258152c95b8d
Using socket.io in Express 4 and express-generator's /bin/www
I wont say what is the best way or not, I dont even like that expression. But I will say it is a way, which helped to keep it clear, extensible, organized and very well modularized.
Specially that each project has its own intrinsic requirements and needs.
Hope these can get to help you as well as it did it for me, get the feeling and understanding of how and what it needs to be done on the project.

Related

What does const {app} =require('module') indicate in node js [duplicate]

This question already has answers here:
What does this symbol mean in JavaScript?
(1 answer)
Javascript object bracket notation ({ Navigation } =) on left side of assign
(5 answers)
Closed 12 months ago.
I am new to node js, while going through few bogs and tutorials I came across one thing different way to import module
For some people use:
const {app} = require('./routes/index');
Few sites follow:
const app= require('./routes/index');
So what is difference between first and second, I am really confused with this and didn't get any proper explanation about this
Short Answer
const app = require('./routes/index');
imports the default value or everything at once from that file
const { app, app2 } = require('./routes/index');
imports specific object/s from that file.
LONG ANSWER
There are two ways to import something from ./routes/index.js.
The first one is,
const app = () => {
console.log("Sample function")
}
// MOST IMPORTANT!
export default app
// Here, we are exporting app from that file as default. We use this normally, when we export only one thing from a file, and in this case, you can use,
const app = require('./routes/index')
As, only one object is getting exported, so you can use this, BUT!
Second Case:-
const app = () => {
console.log("Sample Function 1")
}
const app2 = () => {
console.log("Sample Function 2")
}
export { app, app2 }
// Here, we are exporting two different objects/functions NOT as default. As there are two different object, you need to specify which one to import
Now, to import one of these function, we can use,
const { app2 } = require('./routes/index');
// OR
const { app } = require('./routes/index');
// OR
const { app, app2 } = require('./routes/index');
If you want to import both at once, you can also use,
const app = require('./routes/index')
// The first function can be accessed/called by,
app.app()
// or
app.app2()
But, as this makes it more confusing and takes up more memory, so we don't import all at once normally.

How to break a single node.js file into many?

Hi,
I have an app on node.js which consists of a single file app.js that looks like this:
//variables
app = require("express")();
//many more variables here
//functions
function dosomething {}
//many more functions here
but since its getting a little too long I would like to break it into several files, one for variables only (variables.js) and another one for functions only (functions.js) and load them from app.js like this like when you do it with php
//variables
include(variables.js);
//functions
include(functions.js);
is it even possible to do that? Or I have to include everything in one single file like I do now?
Thank you.
You can use Module.Export to export a separate file, and import it into another file using the require statement. Please check here for details:
https://www.geeksforgeeks.org/import-and-export-in-node-js/
Happy Learning :-)
Importing API Endpoints
You can do this by using app.use(...) and point each endpoint to a specific file like so:
const express = require("express");
const app = express();
// User Functions
app.use("/api/user", require("./routes/api/user"));
//Orders functions
app.use("/api/orders/", require("./routes/api/orders"));
/**
* Express Server Init
*/
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on ${PORT}`));
Then in /routes/api/user/user.js you would have something like:
const express = require("express");
const router = express.Router();
router.post("/create", (req, res) => {
try {
// Create user
} catch (error) {
console.log(error);
res.sendStatus(500);
}
});
module.exports = router;
Add and index.js inside /routes/api/user to point at the user file to keep things pretty when importing (otherwise you have to import it like /routes/api/user/user):
const user = require("./user");
module.exports = user;
Importing Single File
Not sure your use case but variables could be a bad naming convention since these values are more like constants than variables. Either way, you create it like this:
const variables = {
varibleOne: "valueOne",
varibleTwo: "valueTwo",
varibleThree: "valueThree",
};
module.exports = variables;
Then wherever you want to import it you can do:
const variables = require("./variables");
and access it like so variables.variableOneand so on.
Importing functions
You can also import different functions, say you need a file called helper.js where these are commonly functions needed all over you app, you could do something like this:
const twoDecimals = (number, decimal = ",") => {
let val = (Math.round(number * 100) / 100).toFixed(2);
return decimal === "." ? val : val.replace(".", decimal);
};
const getRandomInt = (max) => {
return Math.floor(Math.random() * Math.floor(max));
};
module.exports = { twoDecimals, getRandomInt };
Then wherever you needed you can import it by:
const { twoDecimals } = require("helper.js");
Now you have access to your helper functions anywhere.
You should get help from the JavaScript modular system (preferably COMMONJS).
For example, suppose we have two files:
1-module.js 2-app.js
So now let's create this files
module.js
let name = "hello world";
function printSomething(message) {
console.log(message)
}
//here export all function and variable
module.exports.name = name;
module.exports.printSomething = printSomething
ok so Well now it is enough that "require" this file in main file :
main.js
// we
const {name, printSomething} = require("./module.js");
printSomething(name);
for export all variable You need to create an object and specify your variables as a property:
let host = "localhost"
let dbname = "laravel_8"
let username = "root"
let password = "root"
function doSomething() {
console.log("hello");
}
module.exports = {host, dbname, username, password, doSomething}
so in main file :
const variables = require("./module.js")
//host
let host = variables.host
//dbname
let dbname = variables.dbname
//function doSomething
let doSomething = variables.doSomething;
doSomething()
// or directly
variables.doSomething()
In fact, in php we use the "->" symbol to access properties, and in JavaScript we use "."

module.exports = ({}) vs {}

I work with Koa middleware and in some code I use this code in router:
module.exports = ({router}) => {
//some code
}
if I do:
module.exports = {router} => {
//some code
}
the nodejs app throws an error. So whats the difference between these two different exports except the error part?
{(router)} is just wrong. You should see module.exports as a function so it has () where the arguments go, which in this case is an object {} of the functions you want to export (here router, but it could be multiple just as well.
I think this article gives a very clear explanation: https://www.sitepoint.com/understanding-module-exports-exports-node-js/

How can I split Bot Framework Dialogs in different files

I am trying to make my bot code a bit more manageable and put some dialogs which belong together in different files.
There is an old, similar question here for javascript.
But I am struggling to do the same with Typescript. Probably this is more a general Typescript question as I am a beginner and still am a bit confused about the different import possibilities, but I didn't find any general solution which I was able to apply to this.
What I tried is this:
//testdialog.ts
export default (bot) => {
bot.dialog("/Test", [
(session, args, next) => {
console.log("test".green);
session.send(`Test Dialog triggered`);
},
]).triggerAction({ matches: "test" });
}
and then in app.ts import it similar to this:
import testdialog = require("./testdialog")(bot);
But seems like this seems completely wrong compared to an unnamed import with bot as a parameter in JS like this require('./cars.js')(bot);
In my opinion, you can leverage builder.Library() to achieve your requirement.
//testdialog.ts
import * as builder from 'botbuilder';
export const createLibrary = () => {
let lib = new builder.Library('test');
lib.dialog('test', (session) => {
session.send('this is test dialog');
}).triggerAction({
matches: /test/
});
return lib.clone();
}
//app.ts
import * as restify from 'restify';
import * as builder from 'botbuilder';
import * as testDialog from './testdialog';
let server = restify.createServer({});
server.listen(3978, function () {
console.log('%s listening to %s', server.name, server.url);
})
let connector = new builder.ChatConnector({});
server.post('/api/messages', connector.listen());
let bot = new builder.UniversalBot(connector);
bot.dialog('/', (session) => {
session.send('welcome');
})
bot.library(testDialog.createLibrary())
If you write
// a.js
export default expression;
then you must write
// b.js
import whatever from "./a";
console.log(whatever);
or
// b.js
import * as whatever from "./a";
console.log(whatever.default);
or
// b.js
import whatever = require("./a");
console.log(whatever.default);
But if you write
// a.js
export = expression;
then you must write
// b.js
import whatever = require("./a");
console.log(whatever);
Your syntax is wrong, don't use require, do the import like this:
import * as testdialog from './testdialog';
And then run your bot, like this: testdialog(bot);

How to get list of all routes I am using in restify server

I have a app designed as follows;
//server.js =====================================================
var restify = require('restify'),
route1 = require('./routes/route1),
route2 = require('./routes/route2),
....
....
....
var server = restify.createServer({
name: 'xyz_server'
});
route1(server);
route2(server);
Now each route file looks like belwo
//route1.js =====================================================
module.exports = function(server) {
server.get('/someRoute',function(req,res,next){
//.. do something
});
server.get('/anotherRoute',function(req,res,next){
//..something else
});
};
Now the issue is tht we have dozen's of route files and hundreds of routes in total.
There are multiple developers working on this project and several routes are being added daily.
Is there a function in restify gives me a list of all routes in the system ?
What i am looking for is something like:
server.listAllRoutes();
Is anyone aware of this ?
Try something like this
function listAllRoutes(server){
console.log('GET paths:');
server.router.routes.GET.forEach(
function(value){console.log(value.spec.path);}
);
console.log('PUT paths:');
server.router.routes.PUT.forEach(
function(value){console.log(value.spec.path);}
);
}
listAllRoutes(server);
This should list all GET and PUT paths, adding POST and DELETE should be easy :)
2019 update: server.router.routes is no longer available instead we have server.router.getRoutes() which returns a Map. So we can log all the routes using:
function listAllRoutes(server) {
Object.values(server.router.getRoutes()).forEach(value =>
console.log(
`ENDPOINT REGISTERED :: ${value.method} :: ${server.url}${value.path}`
)
);
}
http://restify.com/docs/server-api/#server
There is a router.getRoutes() method, but it returns an object which is not the best to work with for listing things. You could fiddle around with that to turn it into an array with the shape that you like.
Alternatively, you can access all the routes as an array and then map them, even better if you use a lib like better-console to give you console.table in node. The following is working nicely for me in restify#8.3.0:
import console from 'better-console';
function listRoutes(server) {
const { routes } = server.router._registry._findMyWay; // beware these are probably meant to be private APIs, they could be subject to change
const mapped = routes.map(({ method, path }) => ({ method, path }));
console.table(mapped.sort((a, b) => a.method > b.method));
}

Resources