Routes in express JS taken from DB - javascript

I want to use routes something like this.
For example :
routes.use((req, res, next) => {
/**
* I have an example routes from database and i was passing into variable
* I'm assign fromDb as 'api/test'
*/
var a = fromDb;
next()
})
routes.get(a, (req, res, next) => {
console.log(req.path)
})
I know, a variable in next routes do not get a value from DB cause functional scope. So, any idea for solve this method. I just wondering if I can using modular like this
const DBRoutes = require('lib/example.js')
router.get(DBRoutes, (req, res) => {
console.log(req.path)
})
Any idea for the best method? Thanks

You want to add a route based on content in your database
So you could do the lookup and on success create the route
eg:
dbConnection.lookup(...some query)
.then((pathFromDB) => {
// where pathfromDb = /api/test
routes.get(pathFromDB, (req, res, next) => {
console.log(req.path)
})
});

routes.use((req, res, next) => {
/**
* I have an example routes from database and i was passing into variable
* I'm assign fromDb as 'api/test'
*/
res.locals.fromDb = fromDb;
next()
})
routes.get('/your/route', (req, res, next) => {
console.log(req.path);
console.log(res.locals.fromDb);
});
This is one way of passing variables through different middlewares in express.
I don't think you can dynamically set up routes for express web server. However, routes are set up once during startup. You can get the routes from database at that time.
const route = await routeFromDatabase();
routes.get(route, (req, res, next) => {
console.log(req.path);
console.log(res.locals.fromDb);
});
If you change the database after startup, you will have to restart the node app.
Update 19th Feb 2018: User mentioned the use case as API Gateway. This is worth exploring for such use cases: https://www.express-gateway.io/

Related

How to reference a variable in regex URL?

router.get("/(A|B)/account/", async (req, res) => {});
How to do I reference the (A|B) inside of the async function?
I guess your route responsibility is getting account information of only A or B. So let's change your router path to /account/:name(A|B), then your express router will look like this:
router.get("/account/:name(A|B)", async (req, res) => {
const name = req.params; // A or B
});
Only 2 kinds of requests are handled by this router:
GET /account/A
or
GET /account/B

How to pass req.params as an an argument to a middleware function?

I'm trying to figure out a way to use req.params as an argument in my middleware. Take this (obviously broken) code for example:
router.post('/:myParam', checkSchema(schemas[req.params.myParam]), async (req, res, next) => {
// do stuff
})
The goal here is that I am using express-validator and I load a dynamic schema based on what param is passed. The above code is obviously broken because I don't yet have the scope to access the req variable, I'm just trying to illustrate what I'm trying to accomplish.
if you know the possible params ahead, you could do something like the following:
router.post("/:myParam", checkSchema("soccer"), async (req, res, next) => {});
//checkSchema.JS
const soccerSchema = require("../schemas/soccerSchema");
const swimmingSchema = require("../schemas/swimmingSchema");
module.exports = function (schemaName) {
return (req, res, next) => {
const schemas = { soccer: soccerSchema, swimming: swimmingSchema };
//You can access it here schemas[schemaName]
console.log(schemas[schemaName]);
next();
};
};
You can directly call schemas(req.params.myParam) inside the checkSchema middleware since the middleware will have access to the request object.

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.

Manually chaining Express middleware

I currently use 2 middlewares:
Express-jwt which extracts/validates a JsonWebToken from a request and my own middleware that checks that the JWT contains specific information (permissions).
I want to conditionally use those middlewares together (based on whether there's a specific swagger attribute on a route).
I want to do something like this:
let expressjwt = function(req, res, next) { ... };
let jwtValidator = function(req, res, next) { ... };
app.use((res, req, next) => {
if(req.swagger.someAttribute) {
expressjwt(req, res, jwtValidator(req, res, next));
// The issue here is that jwtValidator will get called even if
// expressjwt produces an error
} else {
next();
}
});
It sounds like the question is - "how do you conditionally call service B only if service A succeeds."
This is one of main goals of promises - it allows you to chain together async calls and have them conditionally "resolve." I can post a code sample if needed.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
I ended up wrapping my first middleware in a Promise using Promise.fromCallback, from memory, something like this:
if (req.swagger.someAttribute) {
Promise.fromCallback(cb => expressjwt(req, res, cb))
.then(() => {
return jwtValidator(req, res, next);
})
.catch(next); // Or deal with the rejection
} else {
next();
}
Promise.fromCallback is useful because next() is only called with arguments if the middleware failed and thus will become promise.reject

It is possible to enhance the express.js req and res variables without using a middleware function?

I'm working in a restful service using express.js and i want to enhance the req and res variables so for example you could write something like
app.use(function (req, res, next) {
res.Ok = function (data) {
res.status(200).send(data);
};
res.InternalError = function (err) {
res.status(500).send(err);
};
});
And later
router.get('/foo', function (req, res) {
res.Ok('foo');
})
This will send 'foo' in the body of the response and set the status code to 200 and is working perfectly.
My first question is if it is possible to add such functionality without a middleware function, lets say in a property or the prototype of the app variable?
The second question is if there are performance issues if you add many functionality with middleware functions at the app level. Are this functions attached to the request and response object per request or once on the application startup?
I know the Sails framework already do this but I'm wondering if they use middleware functions as well.
I keep digging and turns out that the request and response object are exposed in express using the __proto__ property.
var express = require('express'),
app = express();
app.response.__proto__.foo = function (data) {
this.status(200).send(data);
};
And later in the router
router.get('/foo', function (req, res, next) {
res.foo('test');
});
This will print test in your browser so it is possible to add functionality without using any middleware.
Note: I'm sure there are some drawbacks to this approach (overwriting express predefined properties, for example) but for testing purposes and adding very simple functionality I think is slightly better in terms of performance.
I'm not aware of any other way than using middleware. But in my opinion you could do the following to achieve nearly the same thing.
// Some Route
router.get('/foo', function(req, res, next) {
// ...
if(err) {
res.status(500);
return next(err);
}
return res.send('ok');
});
// Another route
router.get('/bar', function(req, res, next) {
// ...
if(badUserId) {
res.status(400);
return next('Invalid userId.');
}
req.result = 'hello';
return next();
});
router.use(function(req, res) {
// I prefer to send the result in the route but an
// approach like this could work
return res.send(req.result);
});
// Error Middleware
router.use(function(err, req, res, next) {
if(res.statusCode === 500) {
// Log the error here
return res.send('Internal server error');
} else {
return res.send(err);
}
});

Categories