Dependency injection to module similar to using a class - javascript

I have this module in TypeScript (or ES2015, the code would be more or less identical) that I want to inject the route to:
var routeObject = express.Router();
var route: string = '/admin';
routeObject.get(route,
(req, res, next) => {
// Do something
return next();
},
jade.View('admin')
);
export default routeObject;
In my application.ts file I import the module and use it like this:
server.Express.use(observationsRoute);
I want to be able to inject the path/route just as if I would have been using a class so that I can write something like this:
server.Express.use(observationsRoute('/admin'));
How could you achieve this?

Export a functions that takes the route as an argument and return the routeObject:
export default (route: string) => {
var routeObject = express.Router();
routeObject.get(route + '/',
(req, res, next) => {
return next();
},
jade.View('admin')
);
return routeObject;
}

Related

vitest Router.use() requires a middleware function but got a undefined

I am trying to unit test my router object in Express but inside the unit test file the object returns undefined
Here is a minimal version of my app
src/config/apiVersion.js
// update major versions here
const version = '/v2'
export default version
src/routes/index.js
import express from 'express'
import {
healthRouter,
healthUrl
} from './health/index.js'
const router = express.Router()
// add new routes here
const allRoutes = [
{
path: healthUrl,
route: healthRouter
}
]
// tell the router to use the routes you added
allRoutes.forEach((route) => {
router.use(route.path, route.route)
})
export default router
src/routes/health/index.js
import express from 'express'
import { healthController } from '../../controllers/health/index.js'
const healthRouter = express.Router()
const healthUrl = '/health'
healthRouter.route('/')
.get(healthController)
export {
healthRouter,
healthUrl
}
src/app.js (note I omitted most of the app.use's such as app.us(cors()) for example
// version is just the string '/v2'
import version from './config/apiVersion.js'
import router from './routes/index.js'
const app = express()
// some other app.use's here omitted like app.use(cors)
// add routes
app.use(`${version}`, router)
// custom 404 to handle non-existent paths/typos on paths
app.use((req, res) => {
res.status(404).send({ error: 'Path does not exist, check for typos. If querying /soap you also need vendor and method in the path' })
})
// custom error handler
app.use((err, req, res) => {
appLogger.error('There was an error: ' + err.stack)
res.status(500).send('Something broke!')
})
export default app
Here is my test file
import router from '../../../src/routes/index.js'
// to make sure the number of routes doesn't change without a new test added
const actualNumberRoutes = 2
describe('router', () => {
it('should return all the routes', () => {
let numberOfRoutes = 0
router.stack.forEach((layer) => {
expect(layer.name).toEqual('router')
numberOfRoutes += 1
})
expect(numberOfRoutes).toEqual(actualNumberRoutes)
})
})
And the error for this file where router is coming up as undefined
Try to provide your app to use routes after importing your respective route files like this.
import healthRouter from 'src/routes/health/index.js';
import router from '../../../src/routes/index.js';
const app=express();
app.use("your_path",router);
app.use("your_health_path",healthRouter);

Making a get request from within a get request

I'm pretty new to node.js and express and I was wondering if there's a way to define a route that calls upon another route simply to collect data and not to completely reroute.
I've got a route set up as follows:
app.get("/databases/list", function(req, res) {
db.listDatabases().then(names => {
res.send(names);
});
});
Subsequently I'd like to have a different route, say:
app.get('/whatever', function(req, res) {
// here I'd like to make a call to retrieve the information from the first route
// then I'd like to do something with that information, I want to stay in the same route.
}
Is this possible?
Expanding #marcobiedermann answer, In your case simply make a controller and and use the FUNCTION in both the routes. You don't need to fetch anything.
/// --- Controller ----
class SimpleController {
constructor(db){
this.db = db;
}
listDatabase(/*maybe optional callback*/){
return this.db.listDatabases();//or something....
}
whatever(/*maybe optional callback*/){
return this.listDatabase()
.then(process)
}
}
/// --- Routes ----
const sController = new SimpleController(db);
app.get("/databases/list", function(req, res) {
sController.ListDatabase().then(names => {
res.send(names);
});
});
app.get('/whatever', function(req, res) {
sController.whatever()
.then(....)
}
Yes this is possible.
You have to fetch the data from your first endpoint.
fetch('/databases/list')
.then( … )
This requires the /databases/list route to be defined before your /whatever route.
However, I would strongly advice you to NOT do this.
You should abstract your logic into a controller and call this controller in both of your routes:
const fetchController = {
fetchData: () => {
return fetch('path/to/data/to/fetch')
.then( … )
// or database call or wherever you might get the data from
}
}
app.get('/databases/list', (req, res) => fetchController.fetchData());
app.get('/whatever', (req, res) => fetchController.fetchData());
app.get("/databases/list", async function(req, res) {
return await db.listDatabases();
});
app.get('/whatever', async function(req, res) {
const result = await fetch('path/databases/list');
console.log(result)
});
It might help you, But it's not recommended way. You can create method (common somewhere in the controller) and use that where ever you need.

Using classes in routing express

I decided to rewrite the code from functions to classes. However, I encountered such a problem that my this undefined
Routing
// router.js
const ExampleController = require('./ExampleController');
const instanceOfExampleController = new ExampleController();
// Require express and other dependencies
app.post('/post-to-login', instanceOfExampleController.login) // An error appears inside the method
And controller
// My Controller
class ExampleController {
// Private method
myPrivateMethod(info) {
return info.toUpperCase();
}
login(req, res, next) {
console.log('----------------------');
console.log(this); // Here "this" equal of undefined!
console.log('----------------------');
const someValue = this.myPrivateMethod(req.body.info); // Not work!
res.send(someValue);
};
}
instanceOfExampleController.login.bind(instanceOfExampleController) will do the trick. The function loses its context once it's being called directly.
Alternatively, you can use:
app.post('/post-to-login', function (req, res, next) {
instanceOfExampleController.login(req, res, next);
});

Extending express.Router

Is there a way to extend express.Router ?
I tried this :
class Test extends express.Router() {
};
But express throws me an error.
Any solution ?
The right way to do it:
class Test extends express.Router {
constructor() {
super();
this.get('/', (req, res) => console.log('test'));
}
};
When you write express.Router() (with parentheses) you already call the constructor and therefore you're trying to extend an object instead of class.
You can't do that because express.Router is a function. I haven't found how to extend a class with the function. But you can use an approach how this http://brianflove.com/2016/03/29/typescript-express-node-js/ or use standard approach with ES2015:
import * as express from 'express';
let router = express.Router();
/* GET home page. */
router.get('/', (req, res, next) => {
res.render('index', { title: 'Express' });
});
export = router;
Maybe something like this:
function My_Router() {
return express.Router.apply(this, arguments);
}
var myRouter = new My_Router();
Excuse me, I don't know English at all. Thanks to Google Translate ...
The constructor function should return a perfect (function, in fact) object of a router.
You can add to this object, whatever you want, or change it as you wish.
Each interface will have a new router object, of course.
It is also necessary to determine the prototype of the constructor function. This happens in line 6.
const Router = require("express").Router;
const myRouter = function () {
const router = Router();
Object.setPrototypeOf(Router, myRouter);
router.myGet = function (path) {
router.get.call(this, arguments);
console.log("is my get function!!");
};
return router;
};
const router_custom = new myRouter();
router_custom.myGet("/", (req, res) => {
res.send("hay!!");
});
May be something like this would help
class YourController extends express.Router{
constructor(){
super()
this.get("/", (req, res)=>{
//do somthing
res.send("return something");
})
this.get("/:id", (req, res)=>{
//do something
res.send("return something);
})
}
}
module.exports = new YourController();
Use It As :
const yourExtendedRouter = require("../controllers/YourController")
router.use("/base-url", yourExtendedRouter)

Exclude route from express middleware

I have a node app sitting like a firewall/dispatcher in front of other micro services and it uses a middleware chain like below:
...
app.use app_lookup
app.use timestamp_validator
app.use request_body
app.use checksum_validator
app.use rateLimiter
app.use whitelist
app.use proxy
...
However for a particular GET route I want to skip all of them except rateLimiter and proxy. Is their a way to set a filter like a Rails before_filter using :except/:only?
Even though there is no build-in middleware filter system in expressjs, you can achieve this in at least two ways.
First method is to mount all middlewares that you want to skip to a regular expression path than includes a negative lookup:
// Skip all middleware except rateLimiter and proxy when route is /example_route
app.use(/\/((?!example_route).)*/, app_lookup);
app.use(/\/((?!example_route).)*/, timestamp_validator);
app.use(/\/((?!example_route).)*/, request_body);
app.use(/\/((?!example_route).)*/, checksum_validator);
app.use(rateLimiter);
app.use(/\/((?!example_route).)*/, whitelist);
app.use(proxy);
Second method, probably more readable and cleaner one, is to wrap your middleware with a small helper function:
var unless = function(path, middleware) {
return function(req, res, next) {
if (path === req.path) {
return next();
} else {
return middleware(req, res, next);
}
};
};
app.use(unless('/example_route', app_lookup));
app.use(unless('/example_route', timestamp_validator));
app.use(unless('/example_route', request_body));
app.use(unless('/example_route', checksum_validator));
app.use(rateLimiter);
app.use(unless('/example_route', whitelist));
app.use(proxy);
If you need more powerfull route matching than simple path === req.path you can use path-to-regexp module that is used internally by Express.
UPDATE :- In express 4.17 req.path returns only '/', so use req.baseUrl :
var unless = function(path, middleware) {
return function(req, res, next) {
if (path === req.baseUrl) {
return next();
} else {
return middleware(req, res, next);
}
};
};
Built upon the answer from #lukaszfiszer as I wanted more than one route excluded.
You can add as many as you want here.
var unless = function(middleware, ...paths) {
return function(req, res, next) {
const pathCheck = paths.some(path => path === req.path);
pathCheck ? next() : middleware(req, res, next);
};
};
app.use(unless(redirectPage, "/user/login", "/user/register"));
Can't add as comment sorry.
You can also skip route like this by putting a condition on req.originalUrl:
app.use(function (req, res, next) {
if (req.originalUrl === '/api/login') {
return next();
} else {
//DO SOMETHING
}
I use this regular expression with success : /^\/(?!path1|pathn).*$/.
There's a lot of good answers here. I needed a slightly different answer though.
I wanted to be able to exclude middleware from all HTTP PUT requests. So I created a more general version of the unless function that allows a predicate to be passed in:
function unless(pred, middleware) {
return (req, res, next) => {
if (pred(req)) {
next(); // Skip this middleware.
}
else {
middleware(req, res, next); // Allow this middleware.
}
}
}
Example usage:
app.use(unless(req => req.method === "PUT", bodyParser.json()));
You can define some routes like below.
app.use(/\/((?!route1|route2).)*/, (req, res, next) => {
//A personal middleware
//code
next();//Will call the app.get(), app.post() or other
});
Here's an example of using path-to-regexp as #lukaszfiszer's answer suggests:
import { RequestHandler } from 'express';
import pathToRegexp from 'path-to-regexp';
const unless = (
paths: pathToRegexp.Path,
middleware: RequestHandler
): RequestHandler => {
const regex = pathToRegexp(paths);
return (req, res, next) =>
regex.exec(req.url) ? next() : middleware(req, res, next);
};
export default unless;
The way I achieved this is by setting up a middleware for a specific path like so
app.use("/routeNeedingAllMiddleware", middleware1);
app.use("/routeNeedingAllMiddleware", middleware2);
app.use("/routeNeedingAllMiddleware", middleware3);
app.use("/routeNeedingAllMiddleware", middleware4);
and then setting up my routes like so
app.post("/routeNeedingAllMiddleware/route1", route1Handler);
app.post("/routeNeedingAllMiddleware/route2", route2Handler);
For the other special route that doesn't need all the middleware, we setup another route like so
app.use("/routeNeedingSomeMiddleware", middleware2);
app.use("/routeNeedingSomeMiddleware", middleware4);
and then setting up the corresponding route like so
app.post("/routeNeedingSomeMiddleware/specialRoute", specialRouteHandler);
The Express documentation for this is available here
In my case I used part of answers posted yet to override original app.use
const unless = ( route, middleware ) => {
return ( req, res, next ) => {
if ( req.originalUrl.startsWith( route + '/' ) ) {
return next();
} else {
return middleware( req, res, next );
}
};
};
const filteredRoute = '/myapi'; // Route to filter and subroute
const origUse = app.use;
app.use = function ( ...callbacks ) {
if ( !callbacks.length ) throw new Error( '.use() method requires at least one function' );
if ( typeof callbacks[0] ==='string' ) {
if ( !( callbacks.length -1 ) ) throw new Error( '.use() method requires at least one function' );
const route = callbacks.shift();
for ( let i = 0; i < callbacks.length; i++ ) {
origUse.call( this, route, unless( filteredRoute, callbacks[i] ) );
}
} else {
for ( let i = 0; i < callbacks.length; i++ ) {
origUse.call( this, unless( filteredRoute, callbacks[i] ) );
}
}
};
Improved upon #Geelie's answer with added types:
import {Request, Response, NextFunction, Handler} from "express";
const unless = (middleware: Handler, ...paths: RegExp[]): Handler => {
return function (req: Request, res: Response, next: NextFunction) {
const pathCheck = paths.some(path => path.test(req.path));
pathCheck ? next() : middleware(req, res, next);
};
};
app.use(unless(redirectPage, new RegExp("/user/login"), new RegExp("/user/register")));

Categories