I'm been looking around everywhere for the last few days for a way to access my socket IO instance running in Express from my routes and have not found a working solution.
The problem here is that I only run my socket IO instance at run-time and my routes and defined before that obviously.
The most promising solution I have found is one that wraps my routes in a function and requires it in my app.js file with whilst passing in the IO instance as an argument like so var routes = require('routes')(io)
Here is my setup:
app.js
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
io.on('connection', function(client) {
console.log(io.sockets.sockets)
});
app.set('port', 7777);
const routes = require('./routes/index')(io);
app.use('/', routes);
app.listen('7777');
routes.js
module.exports = function(io) {
const express = require('express');
const router = express.Router();
router.get('/test', (req, res) => {
console.log(io.sockets.sockets);
});
return router;
}
If I connect to the WebSocket, my io.on event fires and I get a console log of the connected sockets in the form of a fairly large object that contains the socket id's etc.
If however, I get to the route '/test' I get a console log of a blank object: {}. I would imagine this is because the instance of the socket I am passing down to the routes does not have anyone connected to it at the time hence the blank object is returned, but this is just my best guess.
Where I'm a little stuck is how to get the full instance with all live connections to the route.
Alternatively, I've thought of attaching the io instance to the request object like so in order to have access to it in the routes:
server.on('request', function(req, res){
req.io = io;
}
but couldn't quite get it to work, nor am I sure this is the correct approach.
I imagine this must be a common issue so I would love a clear answer on how to work around this and the correct approach to tackle the issue.
EDIT
So I eventually got my above code working, I hit the '/test' endpoint from an AJAX GET request within the chrome extension instead of my just visiting the URL localhost:7777/test. What I can't understand here is why it works with an AJAX request but not page navigation? (The page navigation is done after I make the socket connection in the extension)
Related
i have a node.js back-end with express sever.
i have routes called templete.rpoutes.js:
const express = require("express");
const templateCtrl = require("../controllers/template.controller");
const router = express.Router();
router.route("/createtemplate").post(templateCtrl.createTemplate);
router.route("/uploadFinalTemplate").post(templateCtrl.uploadFinalTemplate);
// beneath are the routes with issue
router.route("/:templateId/productInfos").get(templateCtrl.productInfos);
router.route("/:templateId").get(templateCtrl.Read);
// router.param("templateId", templateCtrl.templateByID);
module.exports = router;
I have the defined method in template controller where i'm just logging stuff. no backend logic is there yet. so I expect when I make a call to these end point, i should get those logs displayed
I have configured express server as:
app.use("/api/template", templateRoute);
app.use("/api/productInfo", productInfoRoute);
app.use('/api/sendInformation', Notify)
// PORT
const port = process.env.PORT || 5000;
const server = app.listen(port, () => {
console.log("Connected to port " + port);
});
Note: the app is express.router() method and the controllers are required correctly and the other end-points work for the same controller.
so when I send a get request from postman to localhost:5000/api/template/5f870cd3b02fd77d0a576a54 I get a 404 and Not found in my console like this: (Plus the server is running)
as described in the documentation https://expressjs.com/en/guide/routing.html#express-router
const router = express.Router();
router.post("/createtemplate",templateCtrl.createTemplate);
router.post("/uploadFinalTemplate",templateCtrl.uploadFinalTemplate);
router.get("/:templateId/productInfos",templateCtrl.productInfos);
router.get("/:templateId",templateCtrl.Read);
So I'm not really sure if the title is descriptive enough, but here is a super simple example.
My site has a public area and a restricted admin area.
example.com/admin (admin home page)
example.com/admin/news (news page)
example.com/admin/posts (posts page)
And because I don't want people who aren't administrators or logged in to be able to access it, I have a simple middleware function to check for cookies.
app.js
const express = require('express');
const app = express();
const authMiddleWere = async (req, res, next) => {
// pseudo-code, do some cookie validity check here
console.log(`Route: ${req.url}`)
if (cookie) {
next();
}
};
const adminRouter = require('./routes/private/home');
const newsRouter = require('./routes/private/news');
const postsRouter = require('./routes/private/posts');
app.use('/admin/', authMiddleWere, adminRouter);
app.use('/admin/news', authMiddleWere, newsRouter);
app.use('/admin/posts', authMiddleWere, postsRouter);
/routes/private/home.js
const express = require('express');
const router = express.Router();
router.get('/', async (req, res, err) => {
res.render('private/home');
});
module.exports = router;
The problem here is that this authMiddleWere function gets called twice when I visit nested paths such as example.com/admin/news which shares the same pattern - it's starting with /admin/......
I can tell that for sure because we are logging the req.url in our middleware function so if I go to example.com/admin it will log out:
Route: /
But if I go to example.com/admin/news it will log out both:
Route: /
Route: /news
So what is causing this and how do I work my way around it? I'm assuming that what I described is the intended behavior of Express.js so I am looking for a way to get around this or (re)structure my code better.
Cheers!
You can use a regex for your route.
app.use(/\/admin$/, authMiddlewear, authRouter);
This will match only routes that end in admin. You may need to handle cases where the route is /admin/ instead of /admin, but iirc, express handles that intelligently.
Well one way you can fix this is by creating a separate route file and splitting everything into a MVC manner. For example:
Inside your main app.js just create a route pointing to the /admin like so:
app.use('/admin', authMiddleWere, require('./src/your-route-to-the-file/admin.route'));
Inside the admin.route file, call your controller like this:
const express = require("express");
const router = express.Router();
const mainAdminCtrl = require("../controllers/admin.controller");
router.get("/news", mainAdminCtrl.adminAuthDisplay);
module.exports = router;
Where the const mainAdminCtrl is your controller and the function adminAuthDisplay is your service.
Essentially, you are splitting your functionality in to a dedicated router, controller and service file. So when you try to access the route /admin, it will look for any suffix inside the router file.
In a case where you want to access the /news endpoint, your API will only make the call once.
If this helps, I can expand my explanation further.
So I wrote up this code on the server side (called app.js):
console.log("Server started. If you're reading this then your computer is still alive."); //Unnecessary test command to make sure everything works.
var express = require("express");
var app = express();
var serv = require("http").Server(app);
const router = express.Router;
app.get("/", function(req, res) {
res.sendFile(__dirname + "/client");
});
app.use("/", router);
app.use("/client", express.static(__dirname + "/client"));
serv.listen(2000);
//Set up server stuff. This isn't touched.
var io = require("socket.io")(serv, {});
io.sockets.on("connection", function(socket) {
console.log("Socket connection"); //This will print to the server, not the developer console in your browser.
});
//Initialize sockets and set up socket listeners. This isn't touched either, except when adding new events.
console.log("Ok"); //Just make sure.
And I have a client:
<!DOCTYPE html>
<html>
<head>
<title>Multiplayer!</title>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
</head>
<body>
<script>
var socket = io();
</script>
</body>
</html>
When I run node app.js in the terminal and then go to localhost:2000 on my browser, it takes a minute or two to load, and then says "localhost didn't send any data" (on Chrome). When I comment out app.use("/", router);, it loads nicely (it doesn't work, because it can't GET /), so I know that there's something wrong with that line, but I don't know what. I looked around the express API documentation but couldn't find anything, so I'm asking here. Thanks!
This code:
const router = express.Router;
app.use("/", router);
is just wrong.
If you wanted to actually create a separate router, you would call the express.Router() constructor like to actually create a new router and then assign some routes to the new router (doc and example code here):
// call router constructor and then assign some routes to it
const router = express.Router();
router.get('/something, function(req, res) {
// handle this route here
});
// hook the router into our instance of express
app.use("/", router);
The crux of the issue is that express.Router is a factory function that creates a router. It's not a router itself. You have to execute it with express.Router() to actually make a router.
The code you had before would not send any response because when it tried to execute express.Router, it would call that function expecting it to be middleware. And, any correctly implemented middleware has to either send a response OR call next() to chain to the next middleware/route in the chain. The express.Router factory function does neither of those (it creates a new Router when called and is not the proper type of function to actually be a route handler all by itself) so the request just gets orphaned at that point, never sending anything to the client and never advancing to the other route handlers.
Eventually the request will time out.
New to node, so my apologies.
I'm working on my app and I want to send the location using socket.io. I've found 1000 examples, but all refer to when express had no routes, and it all was at the app.js. All examples refer to chat applications.
I was able to run an example piecing together several questions I searched but, I don't understand how to get the io that I finally got working on my app.js to interact with my index.js so I can use it with multiple emit parameters. express.io is outdated and I can't find anything current.
On bin/www
/**
* Socket.io
*/
var io = app.io
io.attach( server );
My app.js
var socket_io = require('socket.io');
var app = express();
var io = socket_io();
app.io = io;
So I can use:
io.on('connection', function (socket) {
console.log('IO Ready');
});
I don't know how to use the sockets on my index.js (routes), I can't modularize it.
Thanks in advance.
I have to say, I think the default generated project is bad for a socket.io setup. The node http.Server var is all the way in bin/www and not in app.js.
So the first thing is to move all the relevant stuff from bin/www to app.js. Mainly you just need
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
just like in the socket.io docs.
Now with io in app.js, we can use that when the routes are required. I forgot how exactly the default routes are set up, but I think they set up an app and just export it. Instead, you can set up something like
module.exports = function(app, io) {
io.on('connection', function(socket) {
console.log('connected!');
}
app.get('/foo', function() {
...
}
}
And now when you require the routes, instead of having the default
var index = require('./routes/index');
app.use(index);
or something of that accord, you can just do
require('./routes/index')(app, io);
And that's how you get io into your routes. Or at least how I do it anyway.
I am building a webservice, for which i am using nodejs, phantomjs and expressjs. I am learning all the three.
I want to serve a delayed response to the clients after processing their query. Like for example,
I am processing certain inputs from my client, then, i want to process the data at the backend which will take approx 10 sec on an avg. Then i wanted to serve this page to the client.
Is it possible in node to send multiple responses to the same request or delayed responses so that the template will automatically update the contents.
Or , should i use the same method , like store the json in a file in the server , then serve the page with ajax which will query the page.
please help me. here is the code which i wrote ,
app-server.js(the main file):
// import express module
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
// define all required template files to be served and also define the template engine
app.engine('.html', require('ejs').__express);
app.set('views', __dirname + '/views');
app.set('view engine', 'html');
// Useful modules
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
// import the routes
require('./router')(app);
app.listen(8080);
router.js:
var crypto = require('crypto');
var express = require('express');
module.exports = function (app) {
// define the static routes.
app.use('/static', express.static('./static'));
app.use('/media', express.static('./media'));
//defining the controller.
var parserlib = require('./controller.js')
// Define the home root path
app.get('/', function (req, res) {
// shows the home search page.
res.render('index', {content:'template success'});
});
app.get('/search', function(req, res){
res.redirect('/');
});
app.post('/search', parserlib.parserlib);
}
controller.js:
var crypto = require('crypto');
var path = require('path')
var childProcess = require('child_process')
exports.parserlib= function(req, res){
var output = '';
var url = req.body.search_url;
var childArgs = [
path.join(__dirname, 'external-script.js'),
url,
]
// execute the script in a separate thread.
childProcess.execFile(binPath, childArgs, function(err, stdout, stderr) {
// handle results
console.log(stdout);
output = stdout;
//console.log(err);
//res.send(output);
});
//res.send(output);
};
so , what i want to see is, first send a response to client stating that its loading, then i want to update the with processed data. In other languages its not possible to send multiple responses. Not sure about nodejs.
Also, do i have to store the json output from the processed lib to a file and then use ajax to query ? or is it possible to directly update the json object to the client ?
Thanks
This is just not how HTTP works. The clients won't expect it. This has nothing to do with Node or any other framework. The way to do what you're attempting is to actually send a response that the thing is loading, and then have some other mechanism for reporting state.
As an example, you might design a RESTful API. In that RESTful API you might define a endpoint for creating new things:
POST /api/things
The client would post data to that to create a new thing. The response should be something that provides a location of the newly created resource, for example an HTTP 301 to /api/things/1.
If the user goes to /api/things/1 and the thing isn't done getting made yet, then you can either do a temporary redirect (303) to /api/things/1/status which provides some helpful status information, or just issue a 404.
If you actually want to send back server-side pushes of status information, then you should be looking at WebSockets or a pure Socket API of some kind, neither of which is provided by Express, but both of which are available in Node (checkout the socket.io library and the net core library)