pillarjs / router Goto Github PK
View Code? Open in Web Editor NEWSimple middleware-style router
License: MIT License
Simple middleware-style router
License: MIT License
Hey,
I am sorry if I missed it, but I could not find it in the readme or in any issue:
Does this router support node's http/2 server?
Thanks.
Trying to find the best way to describe router.get / .post / .delete / etc
.
cc @hacksparrow
Hi, seems the res.end(path + '\n')
in the README example throws an error of an undeclared variable.
I want to use thsi module to read params form the routes. Does anyone have a solution for this?
var http = require('http')
var Router = require('router')
var finalhandler = require('finalhandler')
// this example is about the mergeParams option
var opts = { mergeParams: true }
// make a router with out special options
var router = Router(opts)
var server = http.createServer(function onRequest(req, res) {
// set something to be passed into the router
req.params = { type: 'kitten' }
router(req, res, finalhandler(req, res))
})
router.get('/', function (req, res) {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
// with respond with the the params that were passed in
res.end(req.params.type + '\n')
})
// make another router with our options
var handler = Router(opts)
// mount our new router to a route that accepts a param
router.use('/:path', handler)
handler.get('/', function (req, res) {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
// will respond with the param of the router's parent route
res.end(path + '\n')
})
// make our http server listen to connections
server.listen(8080)
> User-Agent: curl/7.43.0
> Accept: */*
> Content-Length: 167
> Content-Type: application/x-www-form-urlencoded
>
* upload completely sent off: 167 out of 167 bytes
< HTTP/1.1 405 Method Not Allowed
< Allow: GET, HEAD, OPTIONS
< Content-Length: 0
< Date: Sun, 13 Mar 2016 02:46:59 GMT
< Connection: keep-alive
Why doesn't it take POST requests anymore? I have router.use(bodyParser.json())
and a router.post('/contact', function(req, res) { ... })
handler.
Whenever router
fails to match a request, it can be one of two reasons:
405
)404
)I cannot seem to figure out a way to differentiate between the two in my finalhandler
and I was hoping that either someone can point me in the right direction or maybe router
can be updated to allow for such things.
Any chance of getting some @types/router typings published? Or are there any floating around that we can make official and publish.
Is there a way of determining if an exact route exists and running some code if it doesn't. For example:
const Router = require('router')
const route = Router().use('/some/route', (res, req, next) => next())
route({url: '/some/nonexistent/route', method: 'get'}, {}, err => {
if (err && err instanceof NoRouteError) {
console.log('No route match')
}
})
Or perhaps something like:
const Router = require('router')
const route = Router().use('/some/route', (res, req, next) => next())
const routeExists = route.test({url: '/some/nonexistent/route', method: 'get'})
console.log(routeExists ? 'ok' : 'No route match')
I'm currently using the library without any HTTP server, just as a router and middleware engine. So req.send()
doesn't exist for me (and shouldn't), I simply call next()
until there is no more middleware in the stack.
Any direction on this would be great.
When I use Express, I can find the client's ip address with req.ip. This doesn't seem to work here. Is there another way?
I am using this for mocking external services and it would be awesome to be able to throw errors (failed expectations) and have the router not catch them. Would you be open to a PR allowing this to be configured?
I am interested to know if you all think it is worth mentioning that this module is compatible with browsers. I threw together an example of using this module in front-end code, and it works perfectly fine.
It is a big deal to say that the module is "Browser Compatible" because of the support issues, but the current implementation works in Chrome & FF (which is all I tested). And after reading most of the code I don't see anything that will clearly break it in other browsers (at least with the required polyfills).
To make the router package useable with TypeScript a .d.ts file is required like the one for Express https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/express
How can I write a custom finalhandler to handle route not found(404) and other errors?
These two properties, layer.path
and layer.params
, should not be exposed as class properties. Their really role is the return value of Layer.prototype.match
.
Line 108 in 51c56e6
As part of the efforts for releasing express
v5, a PR was opened to update path-to-regexp
to 6.2.0
. Since path-to-regexp
is currently targeting node
> 10 explicitly, it'd make sense for router
to deprecate support for node
versions that upstream core libraries do not.
This, of course, would mean that express
as a whole would stop supporting node < 10 from v5. Moving forward, supported node
versions across pillarjs
components should be kept in sync to avoid divergences.
I'm opening the discussion to the entire community to gather feedback and see if they have any input or objections to this.
TypeError: Cannot read property 'toLowerCase' of null
at Route._handles_method (d:\Documents\Github\control-platform-server-consolidation\platform-gateway-server\node_modules\router\lib\route.js:59:21)
at next (d:\Documents\Github\control-platform-server-consolidation\platform-gateway-server\node_modules\router\index.js:254:30)
at csp (d:\Documents\Github\control-platform-server-consolidation\platform-gateway-server\node_modules\helmet-csp\dist\index.js:53:13)
at Layer.handle [as handle_request] (d:\Documents\Github\control-platform-server-consolidation\platform-gateway-server\node_modules\router\lib\layer.js:102:15)
at trim_prefix (d:\Documents\Github\control-platform-server-consolidation\platform-gateway-server\node_modules\router\index.js:330:13)
at d:\Documents\Github\control-platform-server-consolidation\platform-gateway-server\node_modules\router\index.js:294:7
at Function.process_params (d:\Documents\Github\control-platform-server-consolidation\platform-gateway-server\node_modules\router\index.js:349:12)
at next (d:\Documents\Github\control-platform-server-consolidation\platform-gateway-server\node_modules\router\index.js:285:10)
at jsonParser (d:\Documents\Github\control-platform-server-consolidation\platform-gateway-server\node_modules\body-parser\lib\types\json.js:110:7)
at Layer.handle [as handle_request] (d:\Documents\Github\control-platform-server-consolidation\platform-gateway-server\node_modules\router\lib\layer.js:102:15)
continuation from #3
Starting with a discussion in #41, we need to come up with a new api for specifying the http method[s] for a handler. Here are a few examples:
router.methods('GET', 'POST').use('/', function () {});
router.use(['GET', 'POST'], '/', function () {})
router.route('/').methods('GET', 'POST').handler(function () {})
router.route(['GET', 'POST'], '/').handler(function () {})
All of these are fairly consistent from the current API in my opinion, despite being breaking changes in a few cases. Numbers 1 & 3 are not breaking changes AFAIK since they are just adding new methods. Thoughts?
I'd like to discuss the option of emitting a 'next'
event on the request object (or something similar) each time the router enters a new layer.
This would be an example of doing error logging with on('next')
as opposed to relaying on error handling middlewares (and middleware order):
const router = new Router();
function globalAccessAndErrorLogging({logAccess, logError}) {
const errorAlreadyLoggedSet = new Set();
return (req, res, next) => {
logAccess(req);
req.on('close', () => {
errorAlreadyLoggedSet.delete(req);
});
req.on('next', (err) => {
if (err && !errorAlreadyLoggedSet.has(req)) {
errorAlreadyLoggedSet.add(req);
logError(req, err);
}
});
next();
}
}
router.use(globalAccessAndErrorLogging({
logAccess: console.debug,
logError: console.error
}));
The necessity to do something like this comes from an attempt to factor and provide as package any infrastructure related code that, in the company I'm currently working for, we have spread in many applications. Access and error logging is one of them.
In my experience the usage pattern we have is quite common:
mainRouter
.use(logAccess) // any of the usual logging middlewares (morgan / bunyan / pino ...)
.use(apiRoutes)
.use(logErrors); // any of the usual logging middlewares (morgan / bunyan / pino ...)
I noticed many situations in which the error logging is accidentally "shadowed" by another "final" error handler.
Is very hard to spot such situations in code reviews as the modified files does not usually contain anything wrong and any test would pass:
apiRoutes
.use(resource1Routes);
.use(resource2Routes);
.use(handleApiErrors); // this swallows all the errors that would have been logged
The only solution, in the current state, is to rely on discipline: constantly re set-up the logging on each sub-router or force special patterns on all the applications, which makes such a simple problem a complicate coordination effort.
Not sure if this is a new things in node 6, I'm running 6.1.0
, but the core http.methods
include the http BIND
method. When we loop the methods we end up overriding function.bind
on the router instance causing weird behavior. The test that broke was as follows:
it('should reject missing callback', function () {
var router = new Router()
assert.throws(router.bind(router, {}, {}), /argument callback is required/)
})
The intent here is obvious, but it ended up calling the method setup here.
There are three solutions I can see:
.bind
I can make a PR either way but wanted to open the discussion first.
Also for reference, here is the list I got by logging the methods array:
[ 'acl',
'bind',
'checkout',
'connect',
'copy',
'delete',
'get',
'head',
'link',
'lock',
'm-search',
'merge',
'mkactivity',
'mkcalendar',
'mkcol',
'move',
'notify',
'options',
'patch',
'post',
'propfind',
'proppatch',
'purge',
'put',
'rebind',
'report',
'search',
'subscribe',
'trace',
'unbind',
'unlink',
'unlock',
'unsubscribe' ]
This would enable users to optimize their builds using a tool like envify
. I have talked a little before about how I think the debug module is not a great overall solution because it loads and runs even when it is turned off. In this case, my production browser builds get an extra ~4.5kb that they will never use.
I know there are downsides to this, but I just wanted to bring it up in case anyone was interested in this.
This is a reminder to myself (or anyone else) to implement this and give a smooth upgrade path to users of express-async-errors
Curious on the timeline for non-beta release. In the middle of a refactor and this module is at the heart of it.
The one shortcoming with express router is that it does not have a return statement for calling handler.
I wrote a custom request handler that returns a Promise instead of service response right away.
I really need a router that returns the result of call to handler.
I think it will not break anything if you just add return statement to Layer and to Router function.
That would be awesome.
To contine the conversation from #14:
Moving the options responder out into a separate module that is optionally included here seems like a good optimization to me. Also, seems like this is not the domain of that feature anyway.
This would be a breaking change, so probably couldn't land until a 2.0. But I think it would be a simple enough change. Then we could integrate that change into express, even 5.0, by making sure express implements the options handler in the same way it works now.
Hi there!
I'm using a module that has this module as a dependency which I believe is causing that module to crash.
Here's the details:
mulesoft-labs/osprey-router#1
I think its due to an unresolved path in index.js which I can't find in your repo. Your repo code seems totally different then the code in the npm module. :)
From expressjs/express#2241, add the ability to skip the rest of the router. I could have sworn I commented on the issue, but I can't find it anyway.
Use case: Automatically setting up validation middleware (from RAML). I don't want two conflicting routes to run, so exiting on the first handled would be preferable to potentially buggy and confusing behaviour.
Currently the router only discern between path based pattern matching.
In some cases it could be useful to ignore a matched route in the case that during in the middleware dispatch process, providing an extra layer of control flow.
To give you a simple example:
router.use('/users', function (req, res, next) {
if (req.headers.version === '1.0') {
// Ignore the route and continue with the request matching process with other routes
return next.continue()
}
// Perform mad science here with the route as usual
next()
})
Hi,
I think it makes sense to add originalPath
property for consistency.
For example if we mount router as pillarjs
:
/router/issues?q=is
/pillarjs/router/issues?q=is
/router/issues
/pillarjs/router/issues
When I import "Router" in TypeScript, it is not a function and is instead some weird object.
Here is my code:
import * as Router from 'router';
import * as FinalHandler from 'finalhandler';
import * as HTTP from 'http';
import * as FS from 'fs';
import * as MD from './md_convert';
const Port = 80;
var router = Router();
router.get('/', function (req, res) {
res.setHeader('Content-Type', 'text/html; charset=utf-8');
res.end(MD.CompileMarkdown(FS.readFileSync("content/test.md")));
});
var server = HTTP.createServer(function(req, res) {
router(req, res, FinalHandler(req, res));
});
server.listen(Port);
Here is the object:
console.log(Router);
> { Route: [Function: Route],
default: { [Function: Router] Route: [Function: Route] } }
Hi , I'm writing an application that requires re configuring of routes while the app is running. Therefore I need to be able to remove routes that were added previously. I was thinking about doing this by removing the route from router.stack
by filtering for the route with the required path. I was wondering if it would be a useful thing to add to this module?
so something like:
const Router = require('router');
const router = Router()
router.get('/', function (req, res) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('Hello World!')
});
var server = http.createServer(function(req, res) {
router(req, res, finalhandler(req, res))
})
//some event happens instructing to remove the route
setTimeout(() => {
router.remove('/');
}, 1000);
server.listen(3000)
Let say i have tree of files and each of then returns router
// level2.js
const router = Router()
router.route("/user/:id")
.get((req, res) => {})
// level1.js
import level2 from "level2";
const router = Router()
function createRegExpParameter(re, name) {
return (request, response, next, val) => {
if (re.test(String(val))) {
next();
} else {
next(new Error("invalid param"));
}
};
}
router.param("id", createRegExpParameter(/[0-9]+/, "id"));
router.use("/api", level2)
// server.js
import level1 from "level1";
const app = express()
app.use(level1)
in this case when i access /api/user/1
id won't be validated, so it would be good to pass param from top most router (or app) down to bottom most
The README says about param middlewares that they are put in the same stack as .use
.
Ive expected the order of calling to be the same.
In the following example with param
the logs are like: 1, 3, 2
middleware.param('name', async (req, res, next) => {
console.log(1);
next();
});
middleware.get('/:name.:ext', async (req, res, next) => {
console.log(2);
res.end();
next();
});
middleware.param('name', async (req, res, next) => {
console.log(3);
next();
});
In the following example with use
the logs are like: 1, 2, 3
middleware.use(async (req, res, next) => {
console.log(1);
next();
});
middleware.get('/:name.:ext', async (req, res, next) => {
console.log(2);
res.end();
next();
});
middleware.use(async (req, res, next) => {
console.log(3);
next();
});
Is that an expected behavior or a bug ?
any reason not to use the flatten
module? implementation isn't the best but hey.
decode_param
would be nice as a jshttp
module since i tend to use that often. maybe use http-errors
for it if we can somehow make it a TypeError
.
refactoring w/ path-match
would be nice, but you did add extra stuff
Originally posted this over at expressjs #2828.
I think the new title better describes what I was going for. It's basically a convenience method if you ever have to share route handlers in router.method()
calls. There were a few mentions of the issue, but for now I'm posting this for discussion.
I've got two example implementations that I had originally put together below. These were made off of express/master, but if enough people think this would be useful I can put together a PR from this repo.
(Note: the terminology is a bit confused. Originally I called this "private middleware", but then it settled in that old issue to "implicit middleware". Now I'm thinking it's easier to understand as "shared route handlers", but I'd love some input there)
App/Router .with(implicitMiddleware): see this branch
New method to register an array of shared route handlers (implicitly executed middleware). You setup Apps/Routers exactly the same, but there is a new method on instances: .with([fns])
. This is much better than overloading .use
because it won't confuse the current functionality and doesn't allow you to use implicit middleware with a mount-path (doesn't make sense because the route has the path, not the implicit middleware). Note: this now behaves better with declared order as @dougwilson mentioned.
Full example:
var express = require('express');
var app = express();
var middleApp = express();
var leafApp = express();
middleApp.with(function(req, res, next) {
req.someProperty = true;
next();
});
function sendSomeProp(req, res) {
res.send(req.someProperty || 'not set');
}
app.get('/', sendSomeProp);
middleApp.get('/', sendSomeProp);
leafApp.get('/', sendSomeProp);
app.use('/middle', middleApp);
middleApp.use('/leaf', leafApp);
var server = app.listen(1234, function() {});
// RESULTS:
// GET '/': 'not set'
// GET '/middle': 'true'
// GET '/leaf': 'not set'
Implicit Middleware as a create option: see here
This is something closer to what @blakeembrey was suggesting. Instead of some wrapper though, I put the implicit middleware in the options that get passed into the router:
var router = new Router({
caseSensitive: false,
implicitMiddleware: [fn1, fn2, ...]
});
I also changed the create method of an express app to take in an options object similar to Router.
Full example:
var express = require('express');
var app = express();
var middleApp = express({implicitMiddleware: [function(req, res, next) {
req.someProperty = true;
next();
}]});
var leafApp = express();
function sendSomeProp(req, res) {
res.send(req.someProperty || 'not set');
}
app.get('/', sendSomeProp);
middleApp.get('/', sendSomeProp);
leafApp.get('/', sendSomeProp);
app.use('/middle', middleApp);
middleApp.use('/leaf', leafApp);
var server = app.listen(1234, function() {});
// RESULTS:
// GET '/': 'not set'
// GET '/middle': 'true'
// GET '/leaf': 'not set'
Some Notes
Both of the implementations do not handle fallthrough with implicit middleware. Example:
...
middleApp.with(function(req, res, next) {
req.someProperty = 1;
next();
});
function sendSomeProp(req, res) {
res.send(req.someProperty ? '' + req.someProperty : 'not set');
}
app.get('/', sendSomeProp);
middleApp.get('/', function(req, res, next) {
req.someProperty = req.someProperty + 1;
next();
});
middleApp.all('/', sendSomeProp);
leafApp.get('/', sendSomeProp);
...
// RESULTS:
// GET '/': 'not set'
// GET '/middle': '1', should be '2' but the implicit middleware was called twice
// GET '/leaf': 'not set'
It wouldn't be hard to make it so the implicit middleware is only called once during the lifetime of a request, but I feel like this could be more confusing than just knowing the pitfalls of chaining with .all
.
http://example.com/a//b/c
does not match the route for /a/b/c
.
I think it should if strict: false
...
I mean i just need one router but it can be a gate to all route or path. for example :
path : /
router.get('/', function (req, res) {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('no path or param')
})
path : /user
router.get('/', function (req, res) {
var parh = //someway i can get user as path param
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('path : user, but has no param')
})
path : /user/1
router.get('/', function (req, res) {
var parh = //someway i can get user as path param
var param = //someway i can get 1 as param
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.end('path : user and param : 1')
})
and all of that only with /
route as one gate route. in inner handler i wish i can proccess path and param as i want.
Hello everyone. I was wondering if there could be a new release for this awesome library as it seems it's on a death line. We could add other features as by example names routes and url generation. This could be "easy" I guess with latest releases of "path-to-regexp" library. What do you think about it??
I've been working on an example for the docs for #36 but the following didn't work for me:
var finalhandler = require('finalhandler')
var http = require('http')
var Router = require('router')
var router = Router()
router.get('/', function (req, res) {
res.setHeader('Content-Type', 'text/plain; charset=utf-8')
res.json({link: '/abc'})
res.end()
})
var server = http.createServer(function(req, res) {
router(req, res, finalhandler(req, res))
})
server.listen(3000)
Instead I got TypeError: undefined is not a function
It seems like res.json
and res.send
aren't available if a Router is used standalone without Express. Is this by design? I couldn't find anything about it in the docs or in past issues.
Worth saying that this scenario is very not too much common, however in certain specific cases would be handy. Probably this responsibility should be provided natively by the router.
So the idea may be exposing a method to simply remove Layer
instances from the route stack
would be nice. Since Layer
is totally encapsulated as internal implementation detail, it's agnostic to the API consumer, so in terms of querying to find the proper route to remove, it would mimic the natural router matching pipeline (method, regex path).
As discussed in expressjs/express#4084 there is a need for original path on req object.
Example:
router.get("/user/:id", (req, res, next) => {
console.log(req.originalUnparsedPath); // => "/user/:id"
next();
});
originalUnparsedPath
is just an example, but the name would be something along the lines.
I'm really interested in how router works. Are there any references of the implementation?
Hey - was just checking to see if this was available on npm yet, and noticed that the README instructs to npm install router
. So, I just figured I'd make note of that here, although I'm sure that would've become quickly apparent when you did attempt to publish. :)
I need to implement a router similar to this one, but with different path matching semantics. Is there any plan to abstract the core logic into an engine module that other implementations can use?
The single stack middleware system of express seems to be a source of confusion for some developers. This is a proposal to isolate the middleware stack in a router from other routers mounted on the same app.
Current behavior:
Proposed behavior:
The current behavior of app.use()
, which executes its middleware for all the requests to the app would remain the same and should be used for defining global middleware.
I would find it very useful for larger ecosystems or app compose to specify the
query parser
option at router level as well.
It behaves like a mimic of the connect/express middleware, but in some cases it could be useful adding middleware handlers after a route definition.
Why this is restricted? Is there some way to achieve that? Maybe pushing the route to the proper middleware stack?
// This will be executed
router.use('/users/:id', function (req, res, next) {
next()
})
// Configure a new route
router.get('/users/:id')
// This won't be executed
router.use(function (req, res, next) {
next()
})
i'm not sure it makes sense for 4. might be too much change for a stable version.
if for v5, then it would be nice if we just used the latest path-to-regexp
version. i know a lot of people complain about removing trailing splat support - should we readd that to path-to-regexp
?
in the express/x4 the layer.js in express.js. while in the initial commet of this repo it does not implemet it. the commit message states that its imported from express 4 .and it sayed there its altered from express 4 but does not show how .
this is a breaking change which means routes with '*' would not match globally and white-listed “global” functionality would not work.
if this is a drped feature to use fast-slash
only, its not reflected in the docs see
Inspired by expressjs/express#2896, but proposed here because as a breaking change it likely won't make it into Express 4.0 anyway, and Express 5.0 references this.
Instead of using the number of arguments to differentiate between error and normal handlers, is there any reason not to use named functions? Example:
route.get(function(req, res, next) {
next('oops')
}, function(err, req, res, next) {
res.end('Error')
})
Instead, we could say:
route.get(function(req, res, next) {
next('oops')
}, function error(err, req, res) {
res.end('Error')
})
And then just check for this.name
instead of fn.length
in layer.js:63.
Should work in all places where error handlers can be specified here and in express unless I'm overlooking something, and would remove the need (and warnings) for unused parameters.
Could even still allow the old style (but make it deprecated?) to not to break existing code.
Thoughts? :)
Hello everyone. That's more a feature request than an issue.
For the implementation, I think we can add support for named routes first as it'll help make certain routes unique. We could then upon these names have an url generator which would generate the urls for these names routes provided custom parameters.
As for the named routes it could be something like this:
router.get(path, middleware1, [middleware2], {name: 'route.name'});
In this case the route configuration object which hold the route name is the last parameter and is then optional. There may be other options which could be added later.
For the url generation we could have something like:
router.get('post/:year/:month/:slug', handler, {name: 'post.show'});
let url = router.pathFor('post.show', {year: 2019, month: 'oct', slug: 'dummy-post-title'});
// url : 'post/2019/oct/dummy-post-title'
let url = router.pathFor('post.show', {year: 2019, month: 'oct', slug: 'dummy-post-title', order: 'asc'});
// url : 'post/2019/oct/dummy-post-title?order=asc'
Or there could be by example a separate url generator object/class which will receive the router as constructor argument and implement the pathFor method.
I hope this is clear enough.
Thanks
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.