Nodejs and Connect "next" functionality - javascript

It seems that if I want to move to a "next" function in Nodejs (and possibly Javascript in general?) I cannot pass parameters to the next function.
Here is what I mean:
app.get('/webpage', SomeFunction, NextFunction);
function SomeFunction (req, res, next) {
// Do things
next();
}
function NextFunction (req, res) {
// Do other things
}
Now, if in SomeFunction I were to say next(req, res); it does not seem to work. It never gets to the method. Obviously I cannot directly pass parameters...but my question is why? How does the next function know which parameters to use? Is it because they are named the same or does it automatically pass the 1st and 2nd parameters? If NextFunction used blah, bleet instead of req, res would it still work?

This is an intentional aspect of the design of Connect (the node.js middleware that's responsible for this behaviour). The next function your middleware receives is not the next middleware in the stack; it's a function that Connect generates which asks the next middleware to handle it (as well as doing some extra stuff to handle special cases, like when there isn't a "next middleware").
If your middleware should return a response, just do so. If it shouldn't, it's implied that some later middleware should return a response. If you need to pass along data to that later part of the process, you should attach it to an appropriate part of the request object req.
For example, the bundled bodyParser middleware is responsible for populating req.rawBody and req.body based on the contents of the request body. The bundled basicAuth middleware populates req.remoteUser based on the HTTP authentication.
This is the pattern you should try to emulate: a stack of middleware, each of which does a basic incremental thing to process the request. If what you're trying to model doesn't fit into this paradigm, then you should probably just have a single function to handle the request, from which you can call all of your own application logic however you like.

Related

When using Express Router are these two lines equivalent?

I could not find the official docs in a google search. Where are the official API docs for express.Router() and are lines 1 and 2 the same?
If so, is it just a matter of preference on which to use?
const express = require('express');
const router = express.Router();
router.get('path', callback); // line 1
router.route('path').get(callback); // line 2
There are 3 ways with which you can declare routes in your application
These are:
app.get("path", fn)
router.get("path", fn)
router.route("path").get(fn)
All of the above functions are used to generate http request routes in your application.
The app.get is the most basic one and is not recommended for larger projects. The reason being it gives less flexibility in handling routes as compared to the express.router. In express applications you will be dealing with middlewares a lot. Middlewares are functions that are executed before the controller function of your application
For example, take a look at this line
app.get("/user", verifyUser, createUser);`
Above, verifyUser is a middleware function that is called with (Request, Response, Next) arguments by the express framework, you can write your incoming request verification logic in verifyUser and can then callNextFunctionto pass the control to the next function with is thecreateUsercontroller;
You can have as many middlewares as you want in your route declaration.
What if you need to call verifyUser each time a user is created, removed, fetched or modified. For each of the actions, you need to define your routes like this:
app.get("/user", verifyUser, fetchUser);
app.put("/user", verifyUser, updateUser);
app.post("/user", verifyUser, createUser);
app.delete("/user", verifyUser, deleteUser);
In larger applications you need to defined different logics to different entities and routes.
express.router solves the above problem by providing us a flexibility to define what to do when user lands the /user route before passing the request to the middleware.
The above code in express router can be written as follows:
// file: userRoutes.js
const router = express.Router();
router.use((req, res, next) => {
verifyUser(req, res, next);
});
function verifyUser(req, res, next) {
// verification logic here
if (/** verification is successful */)
return next(); // return to the controller
res.status(400).json({ msg: "user does not exists" });
}
router.get("/user", fetchUser);
router.put("/user", updateUser);
router.post("/user", createUser);
router.delete("/user", deleteUser);
module.exports = router;
// app.js
const userRoutes = require("./userRoutes");
app.use(userRoutes);
Things gets really simplified if we chain the http request methods in the following way:
router.route("/user")
.get(fetchUser);
.put(updateUser);
.post(createUser);
.delete(deleteUser);
Note: Above code is for explanation purpose only, there may be syntax errors, the code is not expected to run as it is. User might need to alter the code to make it work.

Passing Node.js middleware to a custom function

Question about express middleware. Say I have a route like the following:
router.route('/replies/:board')
.post(bodyThreadIdVal, textVal, passVal, replyHandler.postReply)
Now let's say I wanted to move the first three middleware arguments from above out of the post method and into a custom method I created in another file, named postReply. How would I go about doing this? I thought maybe using app.use within my postReply method but not sure exactly how or if there is a cleaner way.
I have tried a few methods including
this.postReply = async (req, res, next) => {
app.use(bodyThreadIdVal, textVal, passVal)(req, res, next)
/* additional code */
}
But this seems to cause a recursive loop that rejects with Maximum call stack size exceeded
If the only reason of moving middlewares into a sepparate file is groupping them in one place and making code cleaner and there is no necessity to create a function that will combine your middlewares then I would suggest to group such connected middlewares into an array:
const postReply = [bodyThreadIdVal, textVal, passVal];
router.route('/replies/:board')
.post(...postReply, replyHandler.postReply);
If you will need to add some /* additional code */ just create a new middleware and add it to postReply array. This is definitely much cleaner way.

Winston instance per http request

Lets say I've some REST api server (maybe Express one).
When the life cycle begins (i.e someone GET 'http://foo/bar') there is some data in the Request object.
So let's say I've got something like this:
const method1 = require('some-module').method1;
app.get('/foo/bar', (req, res, next) => {
method1();
});
I want a simple way in the some-module.js to get a winston instance that somehow knows about all relevant data so I don't need every time to pass the request object all over my code.
The trivial solution is to pass the method1 the object and inside do like
method1(req){
winston.info('my message', {requestId: req.id};
}
But this is ugly because I need to change the signature of all my stuff just for logs.
Another option is to make everything a class that extends winston and do something like
app.get('/foo/bar', (req, res, next) => {
const foo = new Foo(new winstonWrapper(req));
});
and than foo.info('msg') will call something like winston.info('msg',{reqId:req.id})
What an elegant way can you suggest to create a winston instance upon request and use it in other modules easily?

Node Express - Route path colon parameter exception

Currently I have two routes in my app:
/invoice/:invoice returns JSON data of an Invoice document from Mongoose
/invoice/preview returns a preview of an invoice inside an HTML template (note that this doesn't always preview an existing invoice, it could also be a non-existing of which its data is supplied via url parameters, which is why the route cannot be /invoice/:invoice/preview)
Question
There should be a better way to declare these two specific routes, because the /invoice/preview route now calls both handlers, since it matches both regexes.
If we were talking in CSS selectors /invoice/:invoice:not(preview) would be the behavior I want. Unfortunately I don't find any documentation for this.
Is there any way to achieve this or any way to improve this endpoint structure?
Declare more specific routes first:
router.get('/invoice/preview', ...);
router.get('/invoice/:invoice', ...);
Express checks routes in order of declaration, so once it has matched a request against /invoice/preview (and provided that its handler sends back a response), the less-specific /invoice/:invoice won't be considered.
Alternatively, if :invoice should always match a specific pattern (say a MongoDB ObjectId), you can limit the route to requests matching that pattern:
router.get('/invoice/:invoice([a-fA-F0-9]{24})', ...);
That pattern doesn't match "preview", so the order wouldn't matter so much in that case.
If this isn't possible, you could create a middleware that would check if req.params.invoice matches "preview" and, if so, would pass along the request further down the handler chain:
let notIfPreview = (req, res, next) => {
if (req.params.invoice === 'preview') return next('route');
next();
};
router.get('/invoice/:invoice', notIfPreview, ...);
router.get('/invoice/preview', ...);
(documented here)

Pass a route segment as callback argument

I'm building my first app in express. Is it possible to somehow pass a route segment as an argument to a callback?
app.get('/connect/:mySegment', myCallback(mySegment));
Specifically, I'm using passport with several strategies for authentication. So rather than doing,
app.get('/connect/twitter',
passport.authorize('twitter')
);
app.get('/connect/facebook',
passport.authorize('facebook')
);
I would like to do something along the lines of...
app.get('/connect/:service', passport.authorize(service));
Of course, you can do
app.get('/connect/:mySegment', function(req, res){
// then you can use req.params.mySegment
});

Categories

Resources