I'm creating a helper method in my code which I can use for authorizing users,
I am creating a function called "usePermissions" following the react way but on the back-end.
The usePermissions() will return true or false based on some x, y and z.
But this method depends on the request object,
so in order to make it work, I'll everywhere have to call
usePermissions(req),
ex:
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = usePermissions(req)
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
Is there a way to package this "usePermissions" helper method with the request object?
I want it to automatically have the request object, I don't want to keep passing it as a variable,
how to make it have the request object without having to pass it as an argument to it, is it possible?
ex:
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = usePermissions() // <-- see, I want to not pass the req object
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
You can create a middleware that will call usePermissions and append result to the Request object, so it will become available in all your handlers without explicitly calling it.
Your middleware code might look something like (read more about using middlewares in Express app)
export function getUsePermissions = (req, res, next) => {
const { verify } = usePermissions(req);
req['verify'] = verify
// moving to the next middleware in chain, now Request object has a verify property
return next()
}
and in your express app, add getUsePermissions middleware
express.use(getUsePermissions);
now you can use extract usePermissions from request object in your handlers:
usersRouter.delete('/users/:userId', async (req, res, next)=>{
const { verify } = req['verify'];
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
Yes, you can use it as middleware and you will get a response, request and next object by default.
import usePermissions from 'hooks/usePermissions'
usersRouter.delete('/users/:userId', usePermissions , async (req, res, next)=>{
if(verify('deleteUsers')) await UserModel.delete(req.params.userId)
// ...
})
usePermissions Function will be like this:
const usePermissions = (request, response, next) => {
const isPermitted = 'you logic';
if (isPermitted) {
next(); // this will automatically call the next middleware
// in your case it will call the next callback in the userRouter
} else {
response.send('You don't have permission'); // just added a sample text for brevity
}
}
You can read more about middlewares here.
Related
I've trying to create a custom logger middleware without any package or library
It's as simple as saving the endpoint, the method and the status code response.
I have the problem when I try to save the status code, since my response has not yet reached the controller. I was trying to understand how morgan does it, because it is the first middleware I use and when my backend responds, it logs the status code.
Is there a simple way without me having to modify all my backend controllers?
Or rather, how can I access the res.status of a controller from this middleware?
const createLog = (req, res, next) => {
const { method, url } = req;
const { statusCode, statusMessage } = res;
console.log(statusCode, statusMessage); // Both null when reach the middleware
next();
};
Try this here:
const createLog = (req, res, next) => {
res.on("finish", function() {
console.log(req.method, decodeURI(req.url), res.statusCode, res.statusMessage);
});
next();
};
I'm struggling a lot trying to understand the logic behind the ExpressJS module, in particular i'm focusing on the implementation of the middlewares chain.
My objective here is to understand how is it possible to implement the logic of a server listening for requests, and once arrived, passes the request packet through an array of functions each of which:
should be able to modify the packet for the next middleware
should be able to send the response back but it should not prevent other middlewares to respond as well, so there should be only one response at the end of the chain (i guess)
should access some function to call in order to run the next middleware in chain, but without the need to pass it the request and response parameters (e.g. just calling "next( )" )
Note on point 1
I imagine that with languages like javascript is possible to pass the object around using concepts like closure, but i would like to understand it in a language-independent way and use javascript features only if it provides clever ways to handle it simply, otherwise i'll write the logic as i was using any other language.
So my big questions are:
How should the request handling be implemented?
How do i allow every middleware to modify the packet?
How do i pass the updated packet to the next middleware?
How do i implement the "next" function to pass to every middleware?
Thank you
Broadly, this is how a chain of middlewares with a next function can be called.
function myMiddleware(req, res, next) {
// do something with req or res
next();
}
const mws = [
myMiddleware,
anotherMiddleware
];
/**
* Calls a chain of middlewares.
*
* mws is an array of middlewares
*/
function callMwChain(req, res, mws) {
if (mws.length === 0) {
// We're done here
return;
}
/**
* Take the first middleware
*/
const firstMw = mws[0];
/**
* Call it, and give it a next function that continues the chain
*/
firstMw(req, res, () => {
callMwChain(req, res, mws.slice(1));
});
}
I've written this as a recursive function, but it can also be rewritten as stack. This is easier though.
Express middlewares have a lot of bonus magic, so their implementation is going to be more complex, but this is broadly how it works.
Note that there's no event-loop in sight.
Here's a little working server that supports middleware and GET routes. It uses a similar calling convention on the route handlers as Express (req, res, next), but has far, far, far fewer features than Express. But, hopefully you can see how the dispatch function cycles through the routes, advancing to the next route only if the previous handler calls next().
You can actually run this code and define routes and play with it (though it is definitely barebones basic):
const http = require('http');
function dispatch(req, res, array, index, cb) {
console.log(req.method, req.url, array, index);
if (array && array.length > index) {
let item = array[index];
if (!item.path || item.path.toLowerCase() === req.url.toLowerCase()) {
item.fn(req, res, function() {
// previous route called next(), so we advance to the next item in the array
++index;
dispatch(req, res, array, index, cb);
});
return;
}
}
cb();
}
const server = http.createServer((req, res) => {
// incoming request here, initiate calling the middleware
dispatch(req, res, server.middleware, 0, function() {
if (req.method === "GET") {
dispatch(req, res, server.gets, 0, function() {
// done with gets here, apparently didn't send a response
res.statusCode = 404;
res.end();
})
} else {
// ... fill in code for other methods here
res.statusCode = 404;
res.end();
}
});
});
// server route implementation
// save arrays for each type of route handler
// upon a request, run all the middleware requests, then advance
// to the routes for the right type of request and see if any of them
// match the URL. If so, run them.
server.middleware = [];
server.gets = [];
server.posts = [];
server.use = function(fn) {
server.middleware.push({path: "", fn: fn});
}
server.get = function(path, fn) {
server.gets.push({path, fn});
}
// route definitions
server.use((req, res, next) => {
console.log(req.url);
req.myCustom = "hi";
next();
});
server.use((req, res, next) => {
console.log(req.myCustom);
next();
});
server.get("/", (req, res, next) => {
res.write("Hello World");
res.end();
});
server.listen(80);
I have a general question on how you handle services and routes in node.js. Would you handle the response directly in the service or would you leave that to the route? Here's what i mean in code
Like this
Route
router.get('/', (req, res, next) ==> {
someService.someMethod(req, res);
});
Service
const someMethod = (req, res) => {
try {
var something = await someOtherMethod(req.body.someParameter);
return res.status(200).send(something.data);
} catch (err) {
return res.status(500).send({msg: err.message});
}
}
Or this
Router
router.get('/', (req, res, next) ==> {
try {
var something = await someService.someMethod(req.body.someParameter);
res.status(200).send(something.data);
} catch (err) {
res.status(500).send({msg: err.message})
}
});
Service
const SomeMethod = (Input) => {
return someOtherMethod(Input);
}
The first way would make the routers much simpler and cleaner especially if the use the service in multiple routes, but on the downside I always need to supply the res and req and I will run into problems if I want to use the service internally. I'm tending to the second method.
How do you design your services?
I would go for router.get('/', RootController)
const RootController = (req, res) => {
// extract what you need from the request
const param = req.body.param;
// calculate what you need in a pure function `businessLogic`
const result = businessLogic(param);
// send the response
return res.send(result);
}
This way you get a separation of concerns - your root controller is responsible only for handling / requests - getting a response for a request. All "business logic" is done in a pure function (you can easily test it without any HTTP request contexts/mocks, it can be reused somewhere else, for example in different controller).
I use the following architecture:
1. Route
2. Controller
3. Services
Your route is the one validating the input, your controller is the one handling all the logics and calling the services and returning the final result to your route.
I am using Express.js as http server. Defined all my routes.
Most endpoints need to verify session before returning a response. E.g. below code serves users in the system and list of services respectively:
function getUsers(req, res, next) {
verifyUser(req, res, next, function () {
//serve users
});
}
function getServices(req, res, next) {
verifyUser(req, res, next, function () {
//serve services
});
}
You probably noticed there is a verifyUser function which validates the session. Which is as below.
function verifyUser(req, res, next, callback) {
var sessionKey = req.cookies.sessionKey;
var user = users.userBySession(sessionKey);
if (user) {
callback(req, res, next, user);
} else {
res.status(401).send({
message: 'Unauthorized'
});
}
}
As you can see I keep passing in req, res and next parameters along with a callback whenever I use this function.
I tried to use apply function to make it easier. Changed my getUsers function like this:
function getUsers(req, res, next) {
verifyUser
.apply(null, arguments, function () {
//serve users
});
}
The problem with this approach is callback is not passed into verifyUser function. And I don't really like passing null as scope with each call.
How can I achieve this by writing less and better code ? Any ideas?
You could use bind to create a 'partial function':
// create bound responseHelper object
var responseHelper = verifyUser.bind(null, req, res, next);
// usage
responseHelper(getUsersCallback); // same as verifyUser(req, res, next, getusersCallBack);
I think you're looking to turn verifyUser into a middleware function.
function verifyUser (req, res, next) {
var user = // yadda yadda session stuff
if (user) {
req.user = user; // [1] what you do to the req object here...
} else {
return res.status(401).send({ message: "No way Smokey Joe"});
/**
* alternatively, do something like
* var err = new Error("Not authorized");
* err.statusCode = 401;
* return next(err);
*
* this will kick off Express' error handling mechanism,
* which you should read about in the docs (see the link below)
*/
}
next();
// very important to call next after this verifyUser has done its job
// if you don't, the next middleware won't go off,
// and the request will just hang
}
function getUsers (req, res, next) {
// [2] will show up on the req object here, assuming you chain these
// two functions together as middleware
}
app.get("/users", verifyUser, getUsers);
app.get("/services", verifyUser, getServices);
// here's a route that needs no session auth, so no need to verifyUser
app.get("/latest-posts", getLatestPosts);
When you tell Express to use a function or attach a function to a route path via get('/my/route', hanlderFun) or some such, you've basically turned handlerFun into a middleware.
You can define however many middleware as handlers on a route as you like, and they'll all execute in turn as long as you keep calling next.
app.post("/checkout", verifyUser, tallyCart, checkInventory, doPayment, sendInvoice);
The job of next is to pass control from the current middelware to the next one. It's an object
You can do other stuff with next, too, which you should read up on in the docs.
http://expressjs.com/en/guide/writing-middleware.html
http://expressjs.com/en/guide/using-middleware.html
The docs on routing have good info on middleware as well:
http://expressjs.com/en/guide/routing.html
For extra credit, check out error handling middleware, too:
http://expressjs.com/en/guide/error-handling.html
In express, everything else held constant, is there a difference between:
app.all('/', mongoProxy(config.mongo.dbUrl, config.mongo.apiKey));
and
app.all('/', function (req, res) {
mongoProxy(config.mongo.dbUrl, config.mongo.apiKey);
});
The former is able to return the return value from mongoProxy while the latter is not, where mongoProxy looks something like this:
module.exports = function(basePath, apiKey) {
basePath = url.parse(basePath);
// Map the request url to the mongolab url
// #Returns a parsed Url object
var mapUrl = module.exports.mapUrl = function(reqUrlString) {
//use the basePath to Parse the URL
return newUrl;
};
var mapRequest = module.exports.mapRequest = function(req) {
var newReq = mapUrl(req.url);
// Make a new request and return it..
return newReq;
};
var proxy = function(req, res, next) {
try {
var options = mapRequest(req);
// Create the request to the db
var dbReq = https.request(options, function(dbRes) {
// Save result
});
// { send result }
res.send(data);
res.end();
});
});
// send request
dbReq.end(JSON.stringify(req.body));
} catch (error) {
//..
}
};
return proxy;
};
The documentation is not clear on explaining the conceptual difference between the two; in the examples I've seen, the former function
app.all('/', mongoProxy(config.mongo.dbUrl, config.mongo.apiKey));
is able to access the req and res object without having it actually passed in as done in the latter, function (req, res).
What is the difference between the two, and is one preferable?
tl;dr
Yes, there is a difference: the first will work, whereas the second will hang (you don't invoke the anonymous function returned by mongoProxy). The first is preferable because it is more idiomatic to express (you're using middleware).
First, note how in mongoProxy, you return proxy, an anonymous function:
module.exports = function(basePath, apiKey) {
/* snip */
var proxy = function(req, res, next) { // <-- here
/* snip */
};
return proxy; // <-- and here
};
Let's break it down:
var proxy = mongoProxy(config.mongo.dbUrl, config.mongo.apiKey)
// proxy is an anonymous function which accepts: (req, res, next)
app.all('/', proxy);
// express will use proxy as the callback (middleware), which means this is the same as:
app.all('/', function (req, res, next) {
proxy(req, res, next)
})
Let's rewrite the second example—which should make clear why it doesn't work:
var proxy = mongoProxy(config.mongo.dbUrl, config.mongo.apiKey)
app.all('/', function (req, res) {
proxy // nothing happens because you don't invoke the function
});
If you want to use the second example, you could invoke proxy with proxy(req, res, next), but this is not idiomatic (in general and especially for express). Express is all about middleware, so use the first example.
Here's another example, which uses a closure (much like your mongoProxy function):
function getPermissionLevelMiddleware (level) {
// returns an anonymous function which verifies users based on `level`
return function (req, res, next) {
if (req.isAuthenticated() && req.user.permission.level > level)
return next()
return res.redirect('/no/permission')
}
}
var isAdmin = getPermissionLevelMiddleware(9000)
// `isAdmin` only allows users with more than 9000 `user.permission.level`
var isPleb = getPermissionLevelMiddleware(1)
// `isPleb` allows users with more than 1 `user.permission.level`
app.get('/admin', isAdmin, function (req, res) {
res.render('admin.jade')
})
Obviously the first one will return result because req and res objects are accessible to it, where as second case you need to send req and res in the method params of mongoProxy. If you won't send, req and res won't be accessible to it. So, for second scenario to work, the method signature need to be changed to :
module.exports = function(basePath, apiKey, req, res) {
I have done