Express JS match route without handling - node.js

Given an instance of a Express JS app or router, is it possible to match a request against the apps configured routes and receive a object that describes the route as registered with the app?
For instance, if a request for /users/1 were to be handled by the application, would it be possible for the app/router instance to programatically check if the app has a route that would satisfy this request given the URI and HTTP method?
Desirable sudo(ish) code:
const app = express();
app.use((req, res, next) => {
const handler = app.match(req);
// {
// 'method': 'GET',
// 'path': '/user/:id', <--- mainly looking for this
// 'handler': <function reference>
// }
next();
});
app.get('/user/:id', (req, res, next) => {
// fetch the user and do something with it
});
...

AFAIK there are no publicly documented Express router endpoints that provide the behavior you are describing based on its 4.x Documentation.
However, you could implement this yourself by creating a custom regular expression validator to check if the req.path string matches any defined path. The downside to this is that you would have to maintain that list separately from what is registered to Express, which might prove to be difficult to maintain.
You may be able to root through the internals of the app object to get the functionality you need, but note the instability of that approach will mean your solution could potentially be broken by non-major updates to Express.

Related

Going from ASP.NET Web API to Node.js with Express

I'm challenged with the task to explore node.js as an alternative to ASP.NET Web API in providing a RESTful API.
I've been developing ASP.NET Web API for some time now and have got used to having certain things within that framework, I'm wondering what others are doing down the node.js route for some of these things: -
What ORM to use against MS SQL instead of Entity Framework
Is there a nice way of handling the routing similarly to what you get in Web API with the route template (routeTemplate: "api/{controller}/{id}")
I'm using Express so far and haven't found a way of applying something like this at a 'top level' so that I can have separate controllers...
That's it so far, I'm sure there are many, many more questions to come but those are my immediate concerns if anybody can help with these?
For ORM the 2 most libraries used are knexjs and sequelize, however, I prefer knex.
For the mapping part, as far as I know, there isn't a way to do it like in the c#. Usually what I do is, in the app.js load a file with my routes index. Here is an example,
In your app.js
app.use('/', require('./backend/routes/index'))
Then, in your routes/index
import express from 'express'
const router = express.Router()
// GET /
router.get('/', function (req, res) {
})
// GET /countries
router.get('/countries', (req, res, next) => {
})
// POST /subscribe
router.post('/subscribe', checkAuth, generalBodyValidation, (req, res, next) => {
})
// All routes to /admin are being solved in the backend/routes/admin/index file
router.use('/admin', require('./backend/routes/admin/index'))
module.exports = router
Your admin/index file can be
import express from 'express'
const router = express.Router()
// POST /admin/login
router.post('/login', (req, res, next) => {
})
module.exports = router
With this, you will be able to have a better structure for your routes.
Hope this asks your questions, if it does mark my answer as correct, if not tell me what you didn't understand :D

Nodejs express: how to use common function needed by different routes logic

How can I add common methods/functions that can be used by different routes built using express in nodejs?
I created a nodejs server that is currently serving 3 APIs. For example:
/cars
/cars/:id
/cars/:id/sellers
All these 3 APIs retrieves data from another external APIs. So, I already have some code that is similar in all of those routes. For example, sending a GET request to the external API endpoint but with different parameters.
For the last API in my list above, I want to enhance it so that it will take optional paramters (lastname, firstname). I could have another API endpoint like this:
/cars/:id/sellers/:lastname&:firstname
But in this case the logic for this and the 3rd API in my list will be almost identical.
I'd like to create some util functions that I can call from any of my API routes with different parameters. I'm not quite sure to do this or where to begin.
Any suggestions?
app.use((req, res, next) => {
//req.someParam
//your logic
next();
});
You can use a middleware like this make sure to add it after your routes has defined, it will work as a common middleware in the routes
router.post('/cars', (req, res, next) => {
// if() some condition
req.someParam = "param";
next();
});

Node dynamic routing when route is undefined

I am new with node and am trying to figure out dynamic routing. I have routes already set up such as
app.route('/users')
.get(dbController.collect)
.post(dbController.insert);
app.route('/users/:userId')
.get(dbController.read)
.put(dbController.update)
.delete(dbController.delete);
I want to be able to do something similar for routes that I have not defined in my code while still letting the routes I have defined work as usual. For example if someone was to send a get request to https://example.com/books/12 it would dynamically run the read function as it does for users.
Express routing allows you to do catch-all routes:
app.all('*', (req, res) => {
console.log('Route is unknown')
// Do something here...
})

API Gateway example for node.js

Looking for a good example of how to implement a node API gateway for a microservice application, I understand the purpose of having a gateway, I am just not sure of how to implement this without just adding another level of RESTful route calls. To me a gateway is supposed to just direct the route to the microservice.
API Gateway port 3000
router.use('/microservicename/*', function (req, res, next) {
**code that will direct to microservice**
});
Microservice1 server.js port 3001
var express = require('express');
var app = express();
var routes = require('./routes/routes');
app.use('/microservicename', routes);
var server = app.listen(3001, function () {
console.log('Server running at http://127.0.0.1:3001/');
});
Microservice1 router.js (3001)
router.get('/route1', function (req, res, next) {
//get route code
});
router.post('/route2', function (req, res, next) {
//post route code
});
router.put('/route3', function (req, res, next) {
//put route code
});
router.delete('/route4', function (req, res, next) {
//delete route code
});
Assuming your microservice is its own http server (if not, then please explain more about its architecture) and assuming your API is designed such that you can easily identify which routes go to which microservice without specifying every single possible route, then you should be able to create one route handler for an entire microservice (or at worst a very small number of route handlers for the entire microservice).
For example, if all requests that start with /api/foo go to the foo microservice, then you should be able to have a single route handler that catches /api/foo/* and proxies that to the microservice. If you have common middleware for all requests (such as middleware that runs or verifies an authentication process), that can be in the stack before the proxy route handlers so it will be invoked for all requests that go to the microservice.
If you don't have a 1-to-1 mapping between incoming API calls and microservice APIs, then you have to create a mapping between the two. Depending upon the level of mismatch, you may be able to do this with a table-driven approach where you specify what matches with what in a table and then one piece of generic code processes all the definitions in the table.

Node.js middleware or not

I'm new to Node.js and I'm migrating a simple site of mine to Node.js mostly as a learning experience.
In all my sites, I like to keep the most relevant information on the site in a "sitemeta" object. This is queried from Redis on each request and if that fails (which it only does if sitemeta gets updated and then it reads it from MySQL instead and saves it in redis so that next request again gets it from redis as redis is much speedier than MySQL).
So, in PHP I would simply add the call for sitemeta in a settings.php file that I always include on top of each file so that the information like home_url or site_mode (that are part of sitemeta object) is always there.
However, now in Node.js I was wondering if this is really the way to go or if there's a better way to actually have this happen as a middleware rather than making like so, at the top of every controller file (router files, really).
//in index.js
var express = require('express');
var router = express.Router();
var site = require('./../lib/sitemeta');
...
//using it for something.
var siteMeta = site.meta();
res.render('index', { title: siteMeta.title });
Or, could I even have the sitemeta instantiated in app.js so that it's queried only once (when the node app.js starts) unless it needs to be updated and if so a refresh can be provoked somehow?
Thanks.
I think the best approach is through a middleware. Wherever you store you metadata (memory, redis, etc), having a middleware will let you inject you siteMeta in all requests, with the capacity to adapt your siteMeta based on the received request (locale, etc). We usually use a lot of small, easily testable middleware functions to inject different data elements for another middleware down the pipeline to process and produce the response.
// with promises
app.use(function(req, res, next){
req.siteMeta = loadSiteMeta();
next();
});
app.get('/endpoint', function(req, res){
// wait for the promise to be fulfilled
req.siteMeta.then(function(siteMeta){
res.render('my-view', { title: siteMeta.title });
});
});
// without promises
app.use(function(req, res, next){
loadSiteMeta(function(err, siteMeta){
req.siteMeta = siteMeta;
next();
});
});
app.get('/endpoint', function(req, res) {
// siteMeta will be populated
res.render('my-view', { title: req.siteMeta.title });
});
The actual implementation of loadSiteMeta will depend on your selected storage. The benefits of using promises instead of classic callbacks here is that, if you have multiple middleware loading different data elements before reaching your final processing function, they will be performed in parallel instead of sequentially. You might want to use Promise.all() to wait for all promises you need in your final function.

Resources