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.
I want to extend this open topic: Add Routes at Runtime (ExpressJs) which sadly didn't help me enough.
I'm working on an application that allows the creation of different API's that runs on NodeJs. The UI looks like this:
As you can see, this piece of code contains two endpoints (GET, POST) and as soon as I press "Save", it creates a .js file located in a path where the Nodejs application is looking for its endpoints (e.g: myProject\dynamicRoutes\rule_test.js).
The problem that I have is that being that the Nodejs server is running while I'm developing the code, I'm not able to invoke these new endpoints unless I restart the server once again (and ExpressJs detects the file).
Is there a way to register new routes while the
NodeJs (ExpressJs) is running?
I tried to do the following things with no luck:
app.js
This works if the server is restarted. I tried to include this library (express-dynamic-router, but not working at runtime.)
//this is dynamic routing function
function handleDynamicRoutes(req,res,next) {
var path = req.path; //http://localhost:8080/api/rule_test
//LoadModules(path)
var controllerPath = path.replace("/api/", "./dynamicRoutes/");
var dynamicController = require(controllerPath);
dynamicRouter.index(dynamicController[req.method]).register(app);
dynamicController[req.method] = function(req, res) {
//invocation
}
next();
}
app.all('*', handleDynamicRoutes);
Finally, I readed this article (#NodeJS / #ExpressJS: Adding routes dynamically at runtime), but I couldn't figure out how this can help me.
I believe that this could be possible somehow, but I feel a bit lost. Anyone knows how can I achieve this? I'm getting a CANNOT GET error, after each file creation.
Disclaimer: please know that it is considered as bad design in terms of stability and security to allow the user or even administrator to inject executable code via web forms. Treat this thread as academic discussion and don't use this code in production!
Look at this simple example which adds new route in runtime:
app.get('/subpage', (req, res) => res.send('Hello subpage'))
So basically new route is being registered when app.get is called, no need to walk through routes directory.
All you need to do is simply load your newly created module and pass your app to module.exports function to register new routes. I guess this one-liner should work just fine (not tested):
require('path/to/new/module')(app)
Is req.params enough for you?
app.get('/basebath/:path, (req,res) => {
const content = require('content/' + req.params.path);
res.send(content);
});
So the user can enter whatever after /basepath, for example
http://www.mywebsite.com/basepath/bergur
The router would then try to get the file content/bergur.js
and send it's contents.
Hello is there any way to use JS environment built in ArangoDB to execute custom JS? I'd like to set up path to my JS files which would be executed instead of foxx application files.
Via GitHub: https://github.com/arangodb/arangodb/issues/1723#issuecomment-183289699
You are correct that modules are cached independently of the routing
cache. Clearing the module cache (or preventing a module from being
cached) is currently not supported.
The actions mechanism is really only intended as an internal API and
only supported for backwards compatibility with early ArangoDB
versions and some edge cases.
As you may have noticed while digging through the ArangoDB source
code, Foxx provides a per-service module cache which is cleared
whenever a Foxx service is reloaded. I would strongly encourage you to
see whether Foxx fits your use case before continuing to dig into the
actions mechanism.
It's actually possible to create a Foxx service with just two files (a
manifest and a controller file) and without using repositories or
models (you can just use the same APIs available in actions).
You just need a controller file like this (e.g. ctrl.js):
'use strict';
const Foxx = require('org/arangodb/foxx');
const ctrl = new Foxx.Controller(applicationContext);
ctrl.get('/', function (req, res) {
res.send('Hello World');
});
with a manifest.json like this:
{
"name": "my-foxx",
"version": "0.0.0",
"controllers": "ctrl.js",
"defaultDocument": "",
"engines": {"arangodb": "^2.8.0"}
}
You can then mount the service (upload a zip bundle) at a path like
/db and access it:
curl http://localhost:8529/_db/_system/db
The upcoming 3.0 release will remove a lot of the existing conceptual
overhead of Foxx which will hopefully make it even easier to get
started with it.
Yes, this can be done with User Actions. Foxx was created as a more comfortable alternative and is likely a better choice for non-trivial applications. The documentation can be intimidating but Foxx services can actually be very lightweight and simple (see my other answer). If you really don't want to use Foxx for this, here's how to do it manually:
First create a virtual module in the _modules system collection:
var db = require('org/arangodb').db;
db._modules.save({
path: '/db:/ownTest',
content: `
exports.do = function (req, res, options, next) {
res.body = 'test';
res.responseCode = 200;
res.contentType = 'text/plain';
};
`
});
Then create a route that uses it:
db._routing.save({
url: '/ourtest',
action: {
controller: 'db://ownTest'
}
});
Finally, tell ArangoDB to update its routing cache so it notices the new route:
require('internal').reloadRouting();
If you install your JavaScript module to the js/common/ or the js/server/ directory you can use the module name (e.g. myOnDiskModule) instead of the virtual module name "db://owntest" in the controller.
For smaller modules you can just define the function inline using callback instead of controller:
db._routing.save({
url: '/hello/echo',
action: {
callback: `
function (req, res) {
res.statusCode = 200;
res.body = require('js-yaml').safeDump({
Hello: 'World',
are: 'you here?'
});
}
`
}
});
Remember to always update the routing cache after changes to the routing collection:
require('internal').reloadRouting();
Note: the callback implementation in 2.8 has a bug that will be fixed in 2.8.3. If you want to apply the fix manually, it's in commit b714dc5.
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.
What are good strategies for role-based authorization in express.js? Especially with express-resource?
With Express-resource there are no handlers, so I think there are three options:
Use a middleware
Pass the authorization function to the resource and check each resource request separately
Check authorization with every request right after authentication
Are there any other solutions?
Group/Role-based authorization is a pretty antique approach. Are there newer methods of access control? If not, how can role-based authorization be applied to node.js? Where to store group-rule relationships (with NoSQL/CouchDB/Redis)?
As an example, the structure:
/
/forums
/forums/threads
Each resource with index, new, create, show, edit update and destroy. Some people can edit/delete etc. threads and forums, some people shouldn't.
I would say that it's hard to solve this in a clean manner using express-resource, since it doesn't allow for route-specific middleware (at least not in a clean way).
I would opt for a similar layout as an express-resource module, but route it with plain old express. Something like this:
// Resource
var forum = {
index: // ...
show: // ...
create: // ...
update: // ...
destroy: // ...
};
// Middleware
var requireRole = function(role) {
return function(req, res, next) {
if('user' in req.session && req.session.user.role === role)
next();
else
res.send(403);
}
};
// Routing
app.get('/forums', forum.index);
app.get('/forums/:id', forum.show);
app.post('/forums', requireRole('moderator'), forum.create); // Only moderators can create forums
app.delete('/forums/:id', requireRole('admin'), forum.destroy); // Only admins can delete forums
UPDATE: There have been ongoing discussions regarding route-specific middleware in express-resource, e.g. here. The prevailing view seems to be to have an array per action, e.g.:
var forums = {
index: [ requireRole('foo'), function(req, res, next) { ... } ]
};
You could take a look through the pull requests and see if there is anything you could use. I totally understand it, of course, if you don't feel comfortable with that. I'm pretty sure we will see something like this in express-resource in the future.
The only other solution I can think of is along the lines of Jan Jongboom's answer, which would be to mount the resources with express-resource, but have middleware attached "outside" of that, something like:
app.delete('*', requireRole('admin')); // Only admins are allowed to delete anything
app.put('/forums/*', requireRole('moderator')); // Only moderators are allowed to update forums
But I regret that this leaks URLs all over the place.
I have been researching the same question and have come across a few good modules. I have been focusing on the node-acl package that can be found here. https://github.com/optimalbits/node_acl.
This package seems to have implemented the ACL pattern in a very understandable way and has provided ways to easily integrate it into your node/express application.
Firstly, you'll want to define your resources, roles, and permissions.
For example, the resources can be:
/
/forums
/forums/threads
The roles can be
public
admin
user
john
jane
In this example, the roles john and jane can map to actual user accounts, but they will inherit all the permissions of the user role.
The permissions on the resources
create
show
update
destroy
Or your standard CRUD operations.
Now that those have been defined, we can take a look at how it would look to set up the acl using node-acl. These notes are derived from the documentation
import the package
var acl = require('acl');
Set up your backend. My app is using mongodb, but the node-acl package does support other storage mechanisms
acl = new acl(new acl.mongodbBackend(dbInstance, prefix));
My app is using mongoose so dbInstance would be replaced with mongoose.connection.db
Now lets add our roles to the ACL. In node-acl, roles are created by giving them permissions. Its like killing two birds with one stone (no birds are actually harmed)
acl.allow('admin', ['/', '/forum', '/forum/threads'], '*');
acl.allow('public', ['/', '/forum', '/forum/threads'], 'show');
acl.allow('user', ['/', '/forum', '/forum/threads'], ['create', 'show']);
Lets assume a new resource is created by john, we will add a new record that allows john to also update and delete that resource.
acl.allow('john', ['/forum/threads/abc123'], ['update', 'delete']);
My application is also using express, so I will use the routing middleware approach to check routes. In my routing configuration, I would add the line
In most express configurations, this looks like for the pos
app.post('/', acl.middleware(), function(req, res, next) {...});
app.post('/forums', acl.middleware(), function(req, res, next) {...});
app.post('/forums/:forumId', acl.middleware(), function(req, res, next) {...});
app.post('/forums/threads', acl.middleware(), function(req, res, next) {...});
app.post('/forums/threads/:threadId', acl.middleware(), function(req, res, next) {...});
When no parameters are passed, this will check if the role defined in req.userId is allowed to execute the http method on the resource identified but the route.
In this example the http method is post, but it will do the same thing for each http idenitified in your configuration.
This raises the question, about the permissions defined earlier. To answer those questions, we would have to change the permissions from
create
show
update
destroy
To the conventional
post
get
put
delete
Although this example shows everything hardcoded, the better practice is to have a management interface for your permissions so they can be created, read, updated, and deleted dynamically without having to modify your code.
I like the node-acl plugins approach as it allows for very fine grained permission-role assignments using a very straight forward and flexible api. There is a lot more in their documentation, my example shows were I am with the package.
Hopefully this helps.
Connect-roles is quite good, simple and the documentation is also very clear.
var user = roles;
app.get('/profile/:id', user.can('edit profile'), function (req, res) {
req.render('profile-edit', { id: req.params.id });
})
app.get('/admin', user.is('admin'), function (req, res) {
res.render('admin');
}
In express you can add a handler that hooks into every operator (http://expressjs.com/guide.html#passing-route control) where you can do precondition validation. Here you can retrieve the role for the user and restrict access based on the HTTP verb (PUT, DELETE, etc.) or the URL (param('op') is 'edit' or so).
app.all('/user/:id/:op?', function(req, res, next){
req.user = users[req.params.id];
if (req.user) {
next();
} else {
next(new Error('cannot find user ' + req.params.id));
}
});
I wrote a module as non-explicit routing middleware. Works well with express-routes.
Gandalf on GitHub
You can try Casbin: https://casbin.org/, it has a Node.js version. It also has a Express.js middleware called express-authz: https://casbin.org/docs/en/middlewares