Adding methods to req? - node.js

librarys like express-validation and passport add methods to the req-variable, like req.assert and req.user. I know I can do this from inside a route:
app.get('/', (req,res,next) => {
req.foo = bar;
next();
}
But how can I do it from a library or external module?

In short: You expose a handler function which your consumers will need to app.use().
Here's the general approach. Consider the following module:
module.exports = function myExtension() {
return function myExtensionHandler(req, res, next) {
// This is called for every request - you can extend req or res here
req.myFun = myFun
// Make sure to call next() in order to continue the
// chain of handlers
return next()
}
}
function myFun() {
// My magic function
}
Now, from a consumer perspective:
const express = require('express')
const myExtension = require('my-extension')
const app = express()
// Here you tell Express to use your extension for incoming requests
app.use(myExtension())
// ...

Related

Koa-Router : Skip the route if the request not in XHR

I have a Rest API made with Koa with some routes, but, at the same time, it will serve my Front (made with a JS framework and its own router).
The fact is, when I access from a browser "localhost/user" I want to display the front but when I reach the same url from fetch / ajax / XMLHttpRequest I want to display a JSON result (the one gave by the Koa-router).
So I would like to enable the /user route from the API only if it's called from XHR.
I did my isXMLHttpRequest middleware like this :
module.exports = async (ctx, next) => {
if(ctx.request.get('X-Requested-With') === 'XMLHttpRequest') {
return next()
}
}
Then, in my koa-router I did something like :
const Router = require('koa-router')
const isXMLHttpRequest = require("#middlewares/isXMLHttpRequest")
const router = new Router()
const user = require("#routes/user")
router.use('/user', isXMLHttpRequest, user.routes(), user.allowedMethods())
And then, it works when I do some XHR request, I have the JSON as planned, but if I try to access the /user from the browser, the API is giving me a Not Found Error and not my front...
I was looking on how to skip the router.use function if the request isn't made in XHR, but I can't find a solution...
I think it's in the middleware else condition, I have to return something, but what can I do to skip the koa-router from giving me 404 ...
Maybe you can help me ?
OK, so if you are using the SAME routes for static and XMLHttpRequests (which is probably not the best strategy), then this could work:
const Koa = require('koa')
const Router = require('koa-router')
const app = module.exports = new Koa();
isXmlRequest = (ctx) => {
// here you could also compare e.g. "accept" header
return (ctx.request.header && ctx.request.header['x-requested-with'] === 'XMLHttpRequest');
}
// static routes
const staticRouter = new Router()
staticRouter.get('/user', (ctx, next) => {
ctx.body = 'OK from static route';
next();
});
// XMLHttpRequest routes
const xmlRouter = new Router()
xmlRouter.get('/user', (ctx, next) => {
if (isXmlRequest(ctx)) {
// serve it
ctx.body = { ok: 'from JSON/XML' }
} else {
// downstream to next handler
next();
}
});
app.use(xmlRouter.routes());
app.use(staticRouter.routes());
const server = app.listen(3000)
This is not using middleware bwcause here you can only allow downstream with next but if there is no next, then this stops. There is no else ;-)
Just for reference
Not sure If I got your question right. So you have a backend that acts like a static web server AND a REST API, right?.
I would try to do it the other way round. Using e.g koa-static (https://www.npmjs.com/package/koa-static) would FIRST try to serve your files and if no matching files are found in your defines public directory, all other routes (so your REST API) are handled. Then you only have to make sure, that endpoint names do not overlap with files you are serving.

How do I share functionality between my expressjs routes?

Suppose I have two routes in my expresjss project: users.js and profiles.js.
users.js:
var express = require('express');
var router = express.Router();
/* GET users listing. */
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { three: 'four', extra: extra } );
});
module.exports = router;
profiles.js:
var express = require('express');
var router = express.Router();
/* GET profiles listing. */
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { one: 'two', extra: extra } );
});
module.exports = router;
Notice how I have something_nice() method there, which ideally I would define in a 'super class' if this were regular OOP like rails controllers.
How do I go about this with node + expressjs? My assumption was I should create a new module, and require it here, but is this the best practice?
You solve it in the same way. Using a class:
class SomethingCool {
somethingNice() {
return 'cool!';
}
}
module.exports = SomethingCool;
In your route:
var express = require('express');
var router = express.Router();
var SomethingCool = require('./something-cool.class');
const something = new SomethingCool();
/* GET profiles listing. */
router.get('/', function(req, res, next) {
var extra = something.somethingNice();
res.json( { one: 'two', extra: extra } );
});
module.exports = router;
Same principles should apply regardless of language (as long as they have support for classes, or class-like objects).
Also you don't even need a class here:
function somethingNice() {
// some logic
}
// inside file1
router.get('/', (req, res, next) => {
const extra = somethingNice();
res.json({ one: 'two', extra });
});
You can reuse functions where you like/need, just ensure they're exported using module.exports (if using in a different file/module).
Have a read over this when you have time:
https://dev.to/santypk4/bulletproof-node-js-project-architecture-4epf
It may be able to answer some of the questions you later have about design, structure and reusing logic in different areas.
EDIT: an explanation on how middleware can help in certain situations.
https://expressjs.com/en/guide/using-middleware.html
function isUserAdmin(user) {
// some logic for determining
}
app.use((req, res, next) => {
if (isUserAdmin(req.user)) {
req.role = 'Admin';
}
next();
});
This is a simplified example, in reality you'd also need to add the req.user.
The thing to understand here, is your ability to use middleware to reuse functionality.
However, if you wanted something a little more specific to the route, then I'd opt to use a class (following the Service pattern from other reference link).
Another (more common) example, consider a logger, it outputs which endpoint was requested and with what method:
// file: service/logger.service.js
class LoggerService {
log(message) {
console.log(message);
}
}
module.exports = MyLogger;
// file: middleware/logger.middleware.js
const logger = new MyLogger();
app.use((req, res, next) => {
const path = req.path;
const method = req.method;
logger.log(`${path} ${method}`);
return next();
});
This way, your route never needs to know about the logger, or what function it has, you can plug an unlimited amount of additional functionality this way.
Although it's more suited for generic tasks, like checking a user has authenticated (for example), or is authorized, but it's certainly not limited to only that type of use.
If you really want a super class, then use a singleton:
class SuperClass {
constructor() {
this.someOtherClass = new UserClass();
this.someSecondClass = new ProjectClass();
}
doSuperWork() {
}
}
module.exports = new SuperClass();
I'll say though, it may not be the best solution (super classes in general).
You can create a function that accepts the router instance as an argument and can implement your common logic inside that function. Javascript uses composition instead of inheritance.
module.exports=function (router){
router.get('/', function(req, res, next) {
var extra = something_nice();
res.json( { one: 'two', extra: extra } );
});
}

How to extend express router function?

Is possible to extend express router function? I need to send soket.io along with response and request.
In one file i have router:
var contoller = require('controller');
...
module.exports = function(app, io) {
router.post('/', controller.postAction);
}
Controller file look something like this:
...
controler.prototype.postAction = function(req, res) {
}
...
So from first file I need to send ´io´ to controller, how can I do that?
you can define the actual postAction function which itself returns a function and pass io to the postAction function, like below:
controler.prototype.postAction = function(io) { // you receive io as an argument
return function(req, res){
// your midleware code here
}
}
and you can require this in your router like:
var contoller = require('controller');
...
module.exports = function(app, io) {
router.post('/', controller.postAction(io)); // note here we pass io
}
What happens here is the function postAction acts as a wrapper which returns the actual middleware, so you can easily pass any argument to it.
Let me know if this is not what you are looking for!

How can I configure koa-router to run common code before any of a certain group of routes?

I'd like to add some functionality to /module that gets executed for any matching route under that directory.
So for a given set of routes:
/module/
/module/page
/module/things
/module/things/:thingid
I want code in a router for /module to run for all of the above routes. /module doesn't render, it just fetches some common data and configures the view context. Then one of the other routes runs and renders the page.
Is this possible with koa-router? Or is there a better package?
Edit:
I think maybe I have to do a nested router and add middleware prior to the nesting like so:
var subRouter = new Router();
subRouter.get('/', function *(next){ doSomething(); });
subRouter.get('/page', function *(next){ doSomething(); });
subRouter.get('/things', function *(next){ doSomething(); });
subRouter.get('/things/thingid', function *(next){ doSomething(); });
mainRouter.use('/module',
function *(next) { doPreRouteSteps(); yield next; },
subRouter.routes()
);
Seems to be working, but I'm not sure if this is an ugly hack or what. If there is a better way, please advise.
You might consider using koa-mount to create a separate app, and then mount it under /module. The sub-app can have any pre- or post- middleware you'd like.
var mount = require('koa-mount');
var koa = require('koa');
var koaRouter = require('koa-router');
var subApp = koa();
var router = koaRouter();
router.get('/page', function *(next){ ... });
subApp.use(function*() { ...pre-route stuff... });
subApp.use(router.routes());
var mainApp = koa();
mainApp.use(mount('/module', subApp));
I like this approach because the way it encourages modularity.
koa-router can do this, i guess better.
router.use('/module/', function *(next) {
// code here
next();
});
router.get('/module/page', function *() {
this.body = 'your view';
});
on this sample, everytime the router encounters '/module'. even if there are trailing or additional params on the url. it will run the first function then proceed to any meet condition.
This is Rei Dien's response updated to 2021. Also you can pass an array of routes or "/" which will call your router.use() before any route. Example with array of routes:
router.use(["/user", "/posts"], async (ctx, next) => {
// Your code before routes...
await next();
});
router.get("/user/likes", async ctx => {
ctx.body = "Your rute code...";
});

Proper way to remove middleware from the Express stack?

Is there a canonical way to remove middleware added with app.use from the stack? It seems that it should be possible to just modify the app.stack array directly, but I am wondering if there is a documented method I should be considering first.
use actually comes from Connect (not Express), and all it really does is push the middleware function onto the app's stack.
So you should be just fine splicing the function out of the array.
However, keep in mind there is no documentation around app.stack nor is there a function to remove middleware. You run the risk of a future version of Connect making changes incompatible with your code.
This is a useful functionality if you are inheriting some unwanted middleware from a framework built on express.
Building on some of the answers that came before me: In express 4.x the middleware can be found in app._router.stack. Note that the middleware are invoked in order.
// app is your express service
console.log(app._router.stack)
// [Layer, Layer, Layer, ...]
Tip: You can search the individual layers for the one you want to remove/move
const middlewareIndex = app._router.stack.findIndex(layer => {
// logic to id the specific middleware
});
Then you can just move/remove them with standard array methods like splice/unshift/etc
// Remove the matched middleware
app._router.stack.splice(middlewareIndex, 1);
There seems to be no built in way to do that, but you can manage to get the same result with a small trick. Create your own array of middleware (let's call it dynamicMiddleware) but don't push that into express, instead push just 1 middleware that will execute all the handlers in dynamicMiddleware asynchronously and in order.
const async = require('async')
// Middleware
const m1 = (req, res, next) => {
// do something here
next();
}
const m2 = (req, res, next) => {
// do something here
next();
}
const m3 = (req, res, next) => {
// do something here
next();
}
let dynamicMiddleware = [m1, m2, m3]
app.use((req, res, next) => {
// execute async handlers one by one
async.eachSeries(
// array to iterate over
dynamicMiddleware,
// iteration function
(handler, callback) => {
// call handler with req, res, and callback as next
handler(req, res, callback)
},
// final callback
(err) => {
if( err ) {
// handle error as needed
} else {
// call next middleware
next()
}
}
);
})
The code is a bit rough as I don't have a chance to test it right now, but the idea should be clear: wrap all dynamic handlers array in 1 middleware, that will loop through the array. And as you add or remove handlers to the array, only the ones left in the array will be called.
You can use the express-dynamic-middleware to make this.
https://github.com/lanbomo/express-dynamic-middleware
Use it like this
const express = require('express');
// import express-dynamic-middleware
const dynamicMiddleware = require('express-dynamic-middleware');
// create auth middleware
const auth = function(req, res, next) {
if (req.get('Authorization') === 'Basic') {
next();
} else {
res.status(401).end('Unauthorization');
}
};
// create dynamic middleware
const dynamic = dynamicMiddleware.create(auth);
// create express app
const app = express();
// use the dynamic middleware
app.use(dynamic.handle());
// unuse auth middleware
dynamic.unuse(auth);
No way of removing a middleware as far as I know. however, you can assign a boolean flag to 'deactivate' a middleware at anytime you want.
let middlewareA_isActivate = true;
// Your middleware code
function(req, res, next) {
if (!middlewareA_isActivate) next();
// .........
}
// Deactivate middleware
middlewareA_isActivate = false;
EDIT :
After reading through ExpressJs (4.x) code, I notice that you can access the middlewares stack via app._router.stack, manipulation goes from there I guess. Still, I think this 'trick' might not be able to work in future Express
P/s: Not tested how Express behaves when manipulate the middlewares stack directly though
Following from the hints above, I've add success with the following on express 4.x. My use case was logging what was coming in with Slack Bolt, so I could capture and then mock it:
// Define a handy function for re-ordering arrays
Array.prototype.move = function(from, to) {
this.splice(to, 0, this.splice(from, 1)[0]);
};
// Use the normal use mechanism, so that 'extra' stuff can be done
// For example, to log further up the order, use app.use(morgan("combined"))
app.use([my-middleware]);
// Now adjust the position of what I just added forward
const numElements = app._router.stack.length;
app._router.stack.move(numElements - 1, 1);
You can use
console.log("Stack after adjustment", app._router.stack)
to confirm the new order is what you want. (For Slack Bolt, I had to use app.receiver.app because the Bolt app wraps the express app.)
We can write like this.
// route outside middleware
route.get("/list", (req, res)=>{
res.send("from listing route");
});
//use middleware
router.use(Middlewares.AuthMiddleware.isValidToken);
//routes inside the middleware
route.post("/create", (req, res)=>{
res.send("from create route");
});
route.delete("/delete", (req, res)=>{
res.send("from delete route");
});
So basically, write routes before injecting middleware into your route.

Resources