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.
Related
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)
As far as I can gather from the Express documentation, when you declare an express.Router(), it creates a single instance of a router that you can then assign a routing path and execute logic with. The documentation says to think of a router like a mini-app for a specific route or routes, which makes sense.
I'm trying to strategize what to wrap my database connection around (using mongodb, let's say via mongoose, but it shouldn't matter). I of course don't want to open a new database connection on every route call, and I assume that if I wrap it around a router only one Router() instance will only be created. In other words, if I went to /routes/index.js, defined a Router() and then opened a database connection, then within it did router.get (or router.post, etc.), I would be opening one database connection when launching the app, not one per hit on that route.
Yet there might be other routes beyond index.js where I want access to the database. So alternatively, could I wrap the database connection around the app.use route handlers and other middleware within the main app.js file, then require('../app') in /routes files and add the database connection to module.exports in app.js, and finally define route logic in other files like /routes/index.js?
I'm a little confused on how to structure everything. Any guidance would be much appreciated.
If you are using mongoose, you can just connect once with some code like this:
mongoose.connect("mongodb://127.0.0.1:27017/test");
mongoose.connection.on('error', console.error.bind(console, 'Db connection error:'));
// Start the server once we have connected to the database.
mongoose.connection.once('open', () => {
console.log('Db connection open.');
app.listen(3000, function () {
console.log('Listening on port 3000');
});
});
And then, if you have a mongoose model named Foo set up like
const Foo = mongoose.model('Foo', fooSchema); // fooSchema is a mongoose.Schema
Then in your route you can use it like so:
const router = express.Router();
const Foo = require('./models/foo');
router
.route('/foos/:foo_id')
.get((req, res)=> {
Foo.findById(req.params.foo_id, (err, foo) => {
if (err) return res.sendStatus(500);
if (!foo) return res.status(404).send('Foo not found.');
res.status(200).json(foo);
});
});
This kind of setup lets mongoose handle connection pooling.
Introduction
I have built some back end functionality in Node (First time using Node). Problem is that the whole thing was built in one page (index.js) so now im following a few basic tutorials and setting out express router middleware and now trying to follow a modular MVC approach,
This code is simple but brakes when I separate into two pages Server.js and config.js. I know its a simple problem but i cant spot it. can someone help spot the problem and maybe improve the structure ?
Problem
I go to http://localhost:8080/about or a different route and I get
Cannot GET /about
rather than the correct print out.
back-end/server.js
var express = require('express');
var app = express();
var port = process.env.PORT || 8080;
// get an instance of router
var router = express.Router();
// START THE SERVER
// ==============================================
app.listen(port);
console.log('Server has started!! ' + port);
back-end/config.js
router.use(function(req, res, next) {
console.log(req.method, req.url);
next();
});
router.get('/', function(req, res) {
res.send('im the home page!');
});
// sample route with a route the way we're used to seeing it
router.get('/sample', function(req, res) {
res.send('this is a sample!');
});
router.get('/about', function(req, res) {
res.send('im the about page!');
});
app.route('/login')
.get(function(req, res) {
res.send('this is the login form');
})
.post(function(req, res) {
console.log('processing'); // shows on console when post is made
res.send('processing the login form!'); // output on postman
});
app.use('/', router);
As #SLaks said in his comment, you need to import (require) your backend/config.js file. But it's not as simple as that...
In node, variables are scoped to the file in which they appear, so if you simply add require('./config') to your server.js file, that's not going to work either, because the router variable in config.js is local to that file - it's not going to know about the router variable in server.js.
The solution to this is to have the config.js file export a function which the server.js file can use to configure stuff. For example
config.js
module.exports = function(router) {
// set up your router here with router.use, etc.
};
server.js
var configure = require('./config');
// after you set up your express router...
configure(router);
// now start listening
While using express 4.x, I am setting the port in my server.js like in the following.
var express = require('express');
var app = express();
...
var port = process.env.PORT || 8080;
app.set('port', port);
...
module.exports = app;
But when I try to access it within my routes file, like the following...
// path to routes file is app/models, hence the '../../'
var app = require('../../server');
// default route
router.get('/', function (req, res) {
res.send('Hello! The API is at http://localhost:' + app.get('port') + '/api');
});
... I get the following error.
TypeError: app.get is not a function
What on earth is going on?
Okay, I have finally figured it out. The app was not properly being set within the routes file because we were previously doing module.exports = app after require('./app/models/routes'); within server.js. So as soon as I moved the exporting of the app to happen before the requiring of the routes file... everything worked!
I don't know exactly what's going on and I don't know if you would like this solution. I had a similar problem as this once.
in the main file i did something like
var express = require('express');
var app = express();
....
var routes = require("./routes/path")
routes(app);
and in routes i did something like
in where you have "./routes/path" file:
module.exports = function(app){
//I got access to app.locals
}
you see I passed along the express app .
basically routes is a function that takes app as a parameter.
I tried app.set("app", app) and I don't think that worked.
It looks like app is not being defined. you could also try something like this.
router.get('/', function (req, res) {
var app =req.app.get("app")
res.send('Hello! The API is at http://localhost:' + app.get("port") + '/api');
});
https://stackoverflow.com/a/15018006/1893672
In your route handling for GET, POST, etc, you should be receiving the 'req' dependency. App should be attached to this so just reference req.app:
router.get('/',function(req,res,next){
console.log(app.get('secret'));
}
No require statement should be necessary.
I'm using expressjs. My issue is that a route is called again if, for some reason, I do not provide a response. In firebug, status of the request will be "pending". Eventually, after the route is called a second time, I get back a response ERR_EMPTY_RESPONSE.
Here's a simple test:
// main.html
<html>
<body>
<span>test</span>
</body>
</html>
<script>
$.get("/test");
</script>
// app.js
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
app.get('/test', function(req, res) {
console.log("I'm called");
});
http.listen(3000, function() {
console.log('Express started');
});
You'll see I'm called when you GET to the route, and then again after about 15-20 seconds.
It seems like Express or Node is somehow causing the route to run a second time. Why is this and how can it be prevented?
EDIT: Made a simple test demonstrating problem.
This probably won't be the solution for most people, but I'm working on a site built with create-react-app and I had the app open in two tabs. For some reason, this was causing duplicate requests on the express side, even though I was only interacting with one tab.
Strange, but closing the second tab solved the problem.
// main.html
<html>
<body>
<span>test</span>
</body>
</html>
<script>
$.get("/test");
</script>
// app.js
var express = require('express');
var app = express();
app.use(express.static(__dirname + '/public'));
app.get('/test', function(req, res) {
res.send("I'm called"); // you have to res.send() method
});
http.listen(3000, function() {
console.log('Express started');
});