What is the difference between get/post/... and use - express

All:
I am new to Express 4 router.
When I tried some login/signup example, I got one question about the .use and .get/.post function:
I saw sometimes it uses:
var express = require('express');
var router = express.Router();
router.get('/hello', function(req, res, next) {
res.send("Welcome");
});
and in main app, we use it like:
app.use("/", router);
While some other time, it uses:
var express = require('express');
var router = express.Router();
//here the router uses .use() function rather than .get/.post
router.use('/hello', function(req, res, next) {
res.send("Welcome");
});
and in main app, we use it like:
app.use("/", router);
So I am wondering what is the difference between them, does the .use() just a general name for all of get/post/put/... together?
I find this post: Difference between app.use and app.get in express.js
But still not feel easy to understand this....
Thanks

In addition to what Jonathan Lonowski said in the posted link, it might help to not compare use to get and post, but to compare it to all because both all and use work regardless of the HTTP verb used while that's obviously not true for get. Everything I'm about to say applies if you replace "all" with "get", it'll just narrow that handler down to a specific HTTP verb.
So, what's the difference between all and use?
app.all will handle incoming requests at the specified URL path regardless of the HTTP verb, just as app.use does. However, how it compares the requested URL to the handler is different. For example:
var r = express.Router();
r.use('/foo', function (...) { ... }); // Route A
r.all('/bar', function (...) { ... }); // Route B
If you make a request to /foo/123 Route A will be run.
If you make a request, however, to /bar/123 Route B will NOT be run.
This is because with HTTP verbs express compares the full path, but with 'use' it only cares about the beginning of the url. Because the URL /foo/123 begins with /foo Route A will run, but because /bar/123 does not match the FULL URL, Route B will not be. Note: You could make .all behave in the same way: r.all('/bar/*', ...), but use is easier and more appropriate for this.
So, what you would tend to mount with one vs the other is different. For example:
var app = express();
var router1 = express.Router();
var router2 = express.Router();
router2.all('*', function (req, res) { ... }); // Must specify a path!
router1.use('/secondary-routes', router2); // Can't do this with all.
app.use(router1); // Look Ma, no path!
Here I've used all to handle a request coming in, where I've used use to mount an entire router. Also, note that the usage of router.METHOD functions require a URL string as the first parameter, while use does not.
At the end of the day, if you:
Want all requests that come in under a given path (or even every request) to use the specified middleware, or
Want to mount an entire sub router/application, or
Want to include a plugin into your application
... Then use is probably what you want.
If you:
Are handling a specific request at a specific URL path (i.e. probably not doing a * match in the URL)
Generally won't be calling next and will instead actually be handling the request
... Then an HTTP verb method (like get, post or all) is probably what you want.

.use is used in 2 cases, middlewares and "modular mountable route handlers".
In your example
router.use('/hello', function(req, res, next) {
res.send("Welcome");
});
This means that any requests sent to /hello will be terminated with "Welcome" and the actual .get attached to /hello will not be called.
So, in short, call use when you need to apply some general middlewares or want to do modular architecture with routers. use can be "used" as request handlers, but you shouldn't because it is not designed for that purpose

Related

How to pass through multiple requests on a route EXPRESS JS

I'm trying to pass through a view as well as a json file so that I can manipulate it within the view.`
var express = require('express');
var router = express.Router();
var data = require('../monsters.json');
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index');
res.json(data);
});
module.exports = router;
`
It passes through the render of the index.ejs view but in the console there is no indication of a json file with it. Nor is there any way to manipulate the file and read it. I'm not sure if I'm just being silly and the method for passing it through doesn't exist.
EDIT: I've now tried this and am pretty sure its being passed through, is there anyway I can verify that the file has been passed through? `
router.get('/', function(req, res, next) {
res.render('index');
res.header("Content-Type",'application/json');
res.sendFile(path.join(__dirname, 'monsters.json'));
});
`
You can't send multiple responses. You get one response per request.
If the file you want to include is JSON, then you can embed it into a <script> tag in your HTML page using your template engine so everything would be in your HTML page and it can be the one response. Then, the JSON will get parsed automatically into a Javascript object that is available to your page's Javascript.
The other alternative is that you have the Javascript in the page make it's own Ajax call to your server to fetch the desired JSON in a separate http request. You would then make a route on your web server for handling that Ajax request and sending back the desired file.
As far as I am aware, you can't respond twice - you can however seemingly provide a callback to res.render(view, locals, callback) so perhaps you can serve it then. Otherwise, I would bake it into the view by generating the view on the backend with the file content as a parameter.
For more info, see;
http://expressjs.com/en/api.html#res.render

How to change express router path without changing URL?

I am statically serving my site from one directory. I have one dynamic route for setting the URL parameter roomCode. I want all routes to this path to serve the root index page without changing the URL on the client (that way I can still use the roomCode in my JavaScript).
Here is what I currently have:
// direct rooms to the index page
app.use('/room/:roomCode([A-Z]{4})', (_, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'))
})
// serve from the dist build
app.use(express.static(path.join(__dirname, 'dist')))
Instead of manually sending the dist/index.html file, I would like to simply change the route path to / for the following middleware and let the static server send the file. Something like this:
// direct rooms to the index page
app.use('/room/:roomCode([A-Z]{4})', (_, res, next) => {
req.path = '/'
next()
})
// serve from the dist build
app.use(express.static(path.join(__dirname, 'dist')))
This way, when the static middleware is reached, it believes the path was /, so it will serve the index page at the root.
Is this possible?
To reroute a request, you must change req.originalUrl to the new route, and then send it to the router handler with app._router.handle(req, res, next).
// direct rooms to the index page
app.use('/room/:roomCode([A-Z]{4})', (req, res, next) => {
// this reroutes the request without a redirect
// so that the clients URL doesn't change
req.originalUrl = '/'
app._router.handle(req, res, next)
})
// serve from the dist build
app.use(express.static(path.join(__dirname, 'dist')))
The documentation for req.originalUrl is a little confusing. It says:
This property is much like req.url; however, it retains the original request URL, allowing you to rewrite req.url freely for internal routing purposes.
This sounds like if you change req.url, it will change how it is routed. However, it does not. It does allow you to change it and then manually check it in latter middleware. But the middleware will still be called based on the original URL. Therefore, we need to overwrite the original URL and send it back through the router.

Express: How to properly define routes?

Suppose I have two routes defined like the following.
The first route is always executed, but the second one is not.
How should I define the routes, so that requests for /about.. are properly routed?
// First route
router.get('/:id', function (req, res) {
// This will always be executed
})
// Second route
router.get('/about/:name', function (req, res) {
// This will not be executed
})
Reverse the order
The routes are stored in a sequence in the order of your router.get() function calls. That is the order the routes are tested for a matching pattern. When you have a route that matches potentially everything, like an /:Id route, then you want to place it last. You then place the static non-changing ancillary pages before it.
In the example below I reverse the order so my static less specific route of "/about/" is checked first and if there is no match then express will compare the request to the next route for a URL match.
// Executed if match is found
router.get('/about/:name', function (req, res) {
})
// No match found on the above routes so try this one
router.get('/:id', function (req, res) {
})
//TODO: Good place for 404 handler...

node.js adding new routes having cannot read property

I'm pretty new to Node, so please bear with me if this is really stupid. I'm just trying to add my own routes for a couple of new pages, which are built on a sample app I got from a book. Here are some code of my app.js file:
var express = require('express'),
routes = require('./routes'),
http = require('http'),
path = require('path'),
mongoose = require('mongoose'),
models = require('./models'),
dbUrl = process.env.MONGOHQ_URL || 'mongodb://#localhost:27017/blog',
db = mongoose.connect(dbUrl, {safe: true}),
and here are all the routes definition:
app.get('/', routes.index);
app.get('/studentLogin', routes.user.studentLogin);
app.post('/studentLogin', routes.user.checkID);
app.get('/studentLoginAfter', routes.tutorRequestForm.selectCourse);
app.get('/login', routes.user.login);
app.post('/login', routes.user.authenticate);
app.get('/logout', routes.user.logout);
app.get('/admin', authorize, routes.article.admin);
app.get('/post', authorize, routes.article.post);
app.post('/post', authorize, routes.article.postArticle);
app.get('/articles/:slug', routes.article.show);
The studentLoginAfter route is the one I'm trying to add, but every time I added it, I got an error like this:
app.get('/studentLoginAfter', routes.tutorRequestForm.selectCourse);
^
TypeError: Cannot read property 'selectCourse' of undefined.
But in my tutorRequestForm.js file, I apparently defined my handler like this:
exports.selectCourse = function (req, res, next) {
res.render('selectCourseForm');
};
Is there anything I missed? I thought it's should be very straightforward for adding new routes in this way, but I'm really frustrated at this point. Please help...
Well, you missed to include your tutorRequestForm.js file. I would recommend you to read a good node.js tutorial and also one for express.js. For me, apparently it seems like you didn't get the real concept behind node and in particular express.js.
Nevertheless, you should do something like this:
// All the requires are here...
var tutorRequestForm = require('pathToTheFile/tutorRequestForm');
// All the routes are defined here...
app.get('/studentLoginAfter', tutorRequestForm.selectCourse);
This should then work for you. You've basically forgotten to include your file. Furthermore you are trying to access your function via the routes variable, which actually reflects another file.

How to intercept node.js express request

In express, I have defined some routes
app.post("/api/v1/client", Client.create);
app.get("/api/v1/client", Client.get);
...
I have defined how to handle requests inside a Client controller. Is there a way that I can do some pre-processing to the requests, before handling them in my controller? I specifically want to check if the API caller is authorized to access the route, using the notion of access levels. Any advice would be appreciated.
You can do what you need in a couple of ways.
This will place a middleware that will be used before hitting the router. Make sure the router is added with app.use() after. Middleware order is important.
app.use(function(req, res, next) {
// Put some preprocessing here.
next();
});
app.use(app.router);
You can also use a route middleware.
var someFunction = function(req, res, next) {
// Put the preprocessing here.
next();
};
app.post("/api/v1/client", someFunction, Client.create);
This will do a preprocessing step for that route.
Note: Make sure your app.use() invokes are before your route definitions. Defining a route automatically adds app.router to the middleware chain, which may put it ahead of the user defined middleware.