Giter Club home page Giter Club logo

router's Introduction

Router middleware for Koa. Maintained by Forward Email and Lad.

build status code style styled with prettier made with lass license

Table of Contents

Features

  • Express-style routing (app.get, app.put, app.post, etc.)
  • Named URL parameters
  • Named routes with URL generation
  • Match routes with specific host
  • Responds to OPTIONS requests with allowed methods
  • Support for 405 Method Not Allowed and 501 Not Implemented
  • Multiple route middleware
  • Multiple and nestable routers
  • async/await support

Migrating to 7 / Koa 2

  • The API has changed to match the new promise-based middleware signature of koa 2. See the koa 2.x readme for more information.
  • Middleware is now always run in the order declared by .use() (or .get(), etc.), which matches Express 4 API.

Install

npm:

npm install @koa/router

Typescript Support

npm install --save-dev @types/koa__router

API Reference

See API Reference for more documentation.

Contributors

Name
Alex Mingoia
@koajs

License

MIT © Alex Mingoia

router's People

Contributors

3imed-jaberi avatar aheckmann avatar alexmingoia avatar dead-horse avatar dominicbarnes avatar etroynov avatar fengmk2 avatar heavenduke avatar ifroz avatar ilkkao avatar jacobmgevans avatar jbielick avatar jergason avatar jeynish avatar joesonw avatar kilianc avatar niftylettuce avatar richardprior avatar secretfader avatar sinewyk avatar smarts avatar tankenstein avatar titanism avatar tj avatar twhitbeck avatar vikramdurai avatar vkhv avatar wachunei avatar yiminghe avatar zacanger avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

router's Issues

Async not working as expected. "Not Found" instantly.

node.js version: 12.8.1

npm/yarn and version: 6.10.2

koa-router version: 7.4.0

koa version: 2.6.2

Code sample:

router.get('/async', async (ctx) => {
  return new Promise((resolve, reject) => {
    console.log('wait...');
    setTimeout(() => {
      ctx.ok('ok after 1s');
      console.log('resolve');
      resolve('asd');
    }, 1000);
  });
});

Expected Behavior:

the request should wait 1s before responding with either 'ok after 1s' or 'asd'

Actual Behavior:

instant response with "Not Found" although timeout is started and stopped as expected. But the results from the timeout not appearing in response since response is already output and connection closed (prematurely IMHO)

The router.use([path], ...fn) might not be correct for path array.

node.js version: 10.15.3

npm/yarn and version: 6.5.0

koa-router version: 8.0.0

koa version: 2.7.4

Code sample:

const Koa = require('koa');
const KoaRouter = require('@koa/router');

const app = new Koa();
const rootRouter = new KoaRouter({
	prefix: '/api'
});
const childRouter = new KoaRouter();

childRouter.get('/test', ctx => {
	ctx.body = 'hello, world!';
});

rootRouter.get('/', ctx => {
	ctx.body = {
		foo: 'bar'
	};
}).use(['/foo', '/bar'], childRouter.routes());

app.use(rootRouter.routes()).listen(80);

Expected Behavior:

Request: [GET] http://127.0.0.1/api/foo/test or http://127.0.0.1/api/bar/test

Response: 200 text/plain hello, world!

Actual Behavior:

Response: 404 text/plain Not found

Additional steps, HTTP request details, or to reproduce the behavior or a test case:

But with string like,

const Koa = require('koa');
const KoaRouter = require('@koa/router');

const app = new Koa();
const rootRouter = new KoaRouter({
	prefix: '/api'
});
const childRouter = new KoaRouter();

childRouter.get('/test', ctx => {
	ctx.body = 'hello, world!';
});

rootRouter.get('/', ctx => {
	ctx.body = {
		foo: 'bar'
	};
}).use('/foo', childRouter.routes());

app.use(rootRouter.routes()).listen(80);

It works. It is correct when request get http://127.0.0.1/api/foo/test.

Additional clues

I watch the router instance stack rootRouter.stack in debugger. Here is the snapshot,

   rootRouter
    Router {opts: Object, methods: Array(7), params: Object, stack: Array(3)}
      [[StableObjectId]]:3
      methods:Array(7) ["HEAD", "OPTIONS", "GET", …]
      opts:Object {prefix: "/api"}
      params:Object {}
      stack:Array(3) [Layer, Layer, Layer]
        [[StableObjectId]]:4
        length:3
        __proto__:Array(0) [, …]
        0:Layer {opts: Object, name: null, methods: Array(2), …}
        1:Layer {opts: Object, name: null, methods: Array(2), …}
          [[StableObjectId]]:6
          methods:Array(2) ["HEAD", "GET"]
          name:null
          opts:Object {end: true, name: null, sensitive: false, …}
          paramNames:Array(0) []
*         path:"/api/bar/api/foo/test"
          regexp:/^\/api\/bar\/api\/foo\/test(?:\/(?=$))?$/i {keys: Array(0), lastIndex: 0}
          stack:Array(1) []
          __proto__:Object {match: , params: , captures: , …}
        2:Layer {opts: Object, name: null, methods: Array(2), …}
          [[StableObjectId]]:6
          methods:Array(2) ["HEAD", "GET"]
          name:null
          opts:Object {end: true, name: null, sensitive: false, …}
          paramNames:Array(0) []
*         path:"/api/bar/api/foo/test"
          regexp:/^\/api\/bar\/api\/foo\/test(?:\/(?=$))?$/i {keys: Array(0), lastIndex: 0}
          stack:Array(1) []
          __proto__:Object {match: , params: , captures: , …}
      __proto__:Object {acl: , bind: , checkout: , …}

It looks very funny at the line marked by '*' .

Router .all throws 404

Hello! (Pay attention: typescript ahead!)
I have a simple server in Koa and I just updated router to it's latest version and naming convention (from koa-router": "8.0.8").

"koa": "2.13.0",
"koa-body": "4.2.0",
"@koa/router": "9.3.1",
"@koa/cors": "3.1.0",
"koa-static": "5.0.0",

Before any route is resolved, I need to check if the context's request has a token. If so, I transform the token to an object and pass it back in the context's state:

import { Context, Next } from 'koa';
import * as Router from '@koa/router';

const auth = new Router();

auth.all(/^\/api\/(.*)(?:\/|$)/, (ctx: Context, next: Next) => {
    const token = ctx.request.headers['authorization'];
    try {
        ctx.state.user = myAwesomeTokenParser(token);
        next();
    } catch (e) {
        ctx.throw(e.statusCode, e.message);
    }
});

export default auth;

the entry point for the Koa server is

import * as koa from 'koa';

const SERVER = new koa();

SERVER.use(public.routes())

SERVER.use(auth.routes())

SERVER.use(hidden.routes())

SERVER.listen(9999);

Because ordering matters in koa router, all the routes defined after the SERVER.use(auth.routes()) are parsed after auth.all(...). Note that public routes are unaffected.

Now the problem is that after the upgrade, every "hidden" route throws 404. Debugging my server show me that hit the auth.all correctly, but the next() seems to throw the request into the void.

Any clue on my error?

Update Wiki

As @jamesaspence mentioned in his comment in an issue of the original repo - the Koa Wiki still points to the old repo and should be updated.

Creating an issue here to increase visibility and to make sure it doesn't slip off the radar.

`ctx.routerPath` causes routes to fail path matches

node.js version: v14.4.0
npm/yarn and version: 6.14.5
@koa/router version: 9.3.1
koa version: 2.13.0

Code sample:

const koa = require('koa');
const app = new koa();
const Router = require('@koa/router');
const router1 = new Router();
const router2 = new Router();

router1.get('/:id/query', async (ctx, next) => {
  console.log('first');
  next();
});

router2.get('/demo/query', async (ctx, next) => {
  console.log('second');
  next();
})

app.use(router1.routes())
app.use(router2.routes())
app.listen(3000);

Expected Behavior:

With a call to /demo/query it is expected that;

router1 will match and first will be output to console
router2 will match and second will be output to console

Actual Behavior:

router1 does match and first is output to console
router2 never matches.

Cause (which I am not sure how to fix):

  1. GET made to /demo/query
  • ctx.path = /demo/query
  • ctx.routerPath = undefined
  • Match path used is ctx.path since ctx.routerPath is undefined - (router.js#L341)
  1. Check for router match to /demo/query - (router.js#L342)
  • Router1 GET of /demo/query matches against regexp /^(?:\/([^\/#\?]+?))\/query[\/#\?]?$/i (/:id/query)
  • layerChain populates ctx.routerPath to match previous path /:id/query (router.js#L366)
  1. Check for next router match to /demo/query
  • ctx.path = /demo/query
  • ctx.routerPath = /:id/query
  • Match path used is ctx.routerPath since ctx.routerPath is set - (router.js#L341)
  1. Check for match to /:id/query - Match FAILS
  • /:id/query does not match against Router2 regexp /^\/demo\/query[\/#\?]?$/i (/demo/query)
  • There is no match so the app stops.

The issue is that ctx.routerPath is being set to the previous matched path and overrides ctx.path for matching. This only appears to cause an issue when a parameter route is used before a similar route without a parameter.

Unfortunately I do not know the codebase enough to provide a solution, but this issue is related to #92, #94 and #97. These issues identify that the problem did not exist in v8.0.8. ctx.routerPath was introduced in v9.0.0 and is when the issues started to be reported.

koa-router v9 not supporting wildcard anymore

router.get('/*', async (ctx) => {
ctx.type = 'html';
ctx.body = fs.createReadStream(${__dirname}/../public/index.html);
});

same issuse ZijianHe/koa-router#527 (comment)
the error where happing like this:

/Users/zz/workspace/zBase/z-app/node_modules/path-to-regexp/dist/index.js:113
throw new TypeError("Unexpected " + nextType + " at " + index + ", expected " + type);
^

TypeError: Unexpected MODIFIER at 1, expected END
at mustConsume (/Users/zz/workspace/zBase/z-app/node_modules/path-to-regexp/src/index.ts:157:11)
at parse (/Users/zz/workspace/zBase/z-app/node_modules/path-to-regexp/src/index.ts:228:5)
at stringToRegexp (/Users/zz/workspace/zBase/z-app/node_modules/path-to-regexp/src/index.ts:494:25)
at pathToRegexp (/Users/zz/workspace/zBase/z-app/node_modules/path-to-regexp/src/index.ts:616:10)
at new Layer (/Users/zz/workspace/zBase/z-app/node_modules/@koa/router/lib/layer.js:44:17)
at Router.register (/Users/zz/workspace/zBase/z-app/node_modules/@koa/router/lib/router.js:561:17)
at Router.get (/Users/zz/workspace/zBase/z-app/node_modules/@koa/router/lib/router.js:199:12)
at Object. (/Users/zz/workspace/zBase/z-app/src/router/index.js:42:8)
at Module._compile (internal/modules/cjs/loader.js:1158:30)
at Module._compile (/Users/zz/workspace/zBase/z-app/node_modules/pirates/lib/index.js:99:24)
at Module._extensions..js (internal/modules/cjs/loader.js:1178:10)

The ctx. _matchedRoute is not the match route.

node.js version: v13.0.1

npm/yarn and version: 6.12.0

koa-router version: 7.4.0 and i tried @koa/router v8.0.2, too.

koa version: 2.7.0

Code sample:

Now I have two routes, one is "/api/order/list" and another is "/api/order/:orderId" ,

const Router = require("koa-router");
const router = new Router({
    "prefix": "/api",
});
router.get('/order/list',controller.getOrderList);
router.get('/order/:orderId',controller.getOrder);

I want to log each request.I want the url I am recording to be the url of my own defined api, not the url directly with params.For example, when I request "http://127.0.0.1:3000/api/order/57d52c52403f2ee865738ec7", I want to record that the url is "/api/order/:orderId" instead of "/api/order/57d52c52403f2ee865738ec7",And when I request "http://127.0.0.1:3000/api/order/list", I want to record that the url is "/api/order/list" instead of "/api/order/:orderId".
But ctx. _matchedRoute is not the route I requested.

Expected Behavior:

when I request "http://127.0.0.1:3000/api/order/list", ctx. _matchedRoute is "/api/order/:orderId"

Actual Behavior:

ctx. _matchedRoute is "/api/order/list"

Additional steps, HTTP request details, or to reproduce the behavior or a test case:

i have the four screenshots.

http:127.0.0.1:3000/api/order/list

image
ctx.matched
image

http:127.0.0.1:3000/api/order/:orderId

image
ctx.matched
image

Router.use() doesn't take in composed Router middleware as expected

It's just a quick mirror of ZijianHe/koa-router#454, which is unresolved but closed.

node.js version: 10.16.0

npm/yarn and version: yarn 1.17.3

@koa/router version: 8.0.6

koa version: 2.11.0

koa-compose version: 4.1.0

Code sample:

const app = new Koa();
const router1 = new Router();
const router2 = new Router();

router2.get('/', ctx => {
  ctx.body = 'not work';
});

router1.use('/', compose([router2.routes(), router2.allowedMethods()]));

app.use(router1.routes()).use(router1.allowedMethods());

Expected Behavior:

Visit '/', get 'not work'

Actual Behavior:

Visit '/', get 'Not Found'

UnhandledPromiseRejectionWarning

Hi,

How to catch errors? The following code produces: UnhandledPromiseRejectionWarning: BadRequestError: blabla

router.get('/', (ctx, next) => {
  ctx.throw(400, 'blabla')
});

node.js version: v12.14.1

npm/yarn and version: 6.13.4

@koa/router version: 8.0.6

koa version: 2.11.0

Code sample:

router.get('/', (ctx, next) => {
  ctx.throw(400, 'blabla')
});

Expected Behavior:

Like without router

app.use((ctx, next) => {
  ctx.throw(400, 'blabla')
});

Response blabla with 400 status code.

Actual Behavior:

(node:90487) UnhandledPromiseRejectionWarning: BadRequestError: balabla

[feature] Add sitemap support

I wonder how we could bring in a sitemap generator into the router. It think sitemaps would have to be generated at the router level, since all routes are (once declared) only know to the router.

But then its also not so easy to generate a sitemap for highly dynamic sites that use a lot of parameterised URLs.

Remove implicit dependence on static definitions of HTTP methods

Cheers to the good folks who have stepped in to maintain this critical peice of the koa ecosystem. I'm ready to push a PR that cleans up this lib's internal handling of http method definitions, but I want to solicit some community feedback first. (ie this a proposal, not a complaint 👍)

We have some work to do re "allowed"/"standard" http methods. Get ready for some hair-splitting. If you're already bored, stop reading here. Currently, this lib:

  • depends on the (aptly and descriptively named) "methods" external package, which exposes an array of strings representing http methods, which it gets from either node's builtin http module or a hard-coded list if the former is unavailable.
  • accepts a separate consumer-supplied array of allowed http methods passed to the contructor (although this doesn't appear to be documented)
  • maintains a per-layer list of methods that are actually used by declared routes

Let's ignore for now the cliche topic of minimizing external dependencies and the fact that many take the number of deps into consideration when evaluating a project's integrity/stability and suitability for use in their own projects.

Open Questions:

  • Do we want this lib to implicitly depend on a nodejs execution environment? And on a mutable global at that?

  • Do we want this lib to be restricted to standards-compliant http? Are we comfortable with the fact that there are esoteric use cases for which Koa, due its generality, works just fine but this lib will not?

  • Are we comfortable, from a documentation and API contract perspective, with the fact that dynamic details of the runtime environment are able to significantly alter the top-level API that this lib exposes? (ie the method signature of Router class instances)

  • Are we misleading consumers and violating the principle of least surprise by having the constructor accept a config/options arg of allowed http methods which is then not respected (consistently)?

  • Are we similarly violating the principle of least surprise with the way that .all() routes are defined? What does a consumer expect to happen when they bind a route definition to "all" http methods? (I doubt they'd expect it to match MKCALENDAR but not a hypothetical custom method they explicitly passed as a constructor arg)

Observations:

  • this lib will instantly throw in a runtime where the commonjs module http isn't resolvable

  • an exotic, non-standard or custom http module has the potential to make this lib's behavior undefined

  • manual runtime mutation of node's builtin http.METHODS global constant has the potential to make this lib's behavior undefined

  • the existence of any value whatsoever for http.METHODS will prevent the well-known defaults (that are hard-coded) from being referenced at all

  • It is possible for the values returned by a router's allowedMethods() to be inconsistent with requests that the router will actually route

I propose that we:

  • ditch the methods dependency, along with all assumptions that a builtin http module is present, that it is in use, and that it constitutes an authoratative enum of all allowed http methods

  • decouple this lib from any particular protocol-level construct (such as the HTTP spec itself and its list of standard supported methods) in favor of a more abstract, protocol-agnostic "Koa-like" approach that takes any possible Koa context object with whatever arbitrary value it may have for request.method

    • also with the understanding that it is not unreasonable to expect request.method to be manually modified before being passed to koa-router
    • ...and the understanding that custom implementation-specific (and even application-specific) http methods are not uncommon and not unreasonable (vendor-specific CalDAV implementations, ownCloud's WebDAV implementation, etc)

404 error when defining routes: Does order matter in defining routes?

node.js version: v11.3.0

npm/yarn and version: 6.14.5

@koa/router version: 7.4.0

koa version: 2.5.2

Code sample:

in my routes file I have this

router.get('/entity/:id', ApiClass.getById);
router.post('/entitylist', ApiClass.getList);
router.get('/entity/number/:number', ApiClass.getId);

If I try to add a new route (so it looks like below) the last API (/entity/status/) doesn't get picked up and I end up with a 404 error.

router.get('/entity/:id', ApiClass.getById);
router.post('/entitylist', ApiClass.getList);
router.get('/entity/number/:number', ApiClass.getId);
router.get('/entity/status/', ApiClass.getStatus);

However, if I set it at the beginning of these set of routes (like below) it works just as expected.

router.get('/entity/status/', ApiClass.getStatus);
router.get('/entity/:id', ApiClass.getById);
router.post('/entitylist', ApiClass.getList);
router.get('/entity/number/:number', ApiClass.getId);

My question: is the order a factor in whether routes are well defined? what is the correct order for these definitions?

How to reroute a request

Looking for some guidance on how to "reroute" a request from a route handler. Basically I'd like to have a route that makes some adjustments to the request (change ctx.url) returns control back to the router to try to find another matching route.

Express had a concept of calling next('route') to do something like this, but I can't find anything similar int the docs. Is this supported?

Thanks!

Update - looks like I can accomplish this in a route like this, but it feels hacky

ctx.method = newMethod;
ctx.url = newUrl;
ctx.request.body = newBody;
await ctx.router.routes()(ctx, next);

ALSO - interestingly, if I use a named param handler in the route that is trying to redirect to another, then this solution above does not work. It will call the route correctly, but execution of the middleware stack jumps ahead.

middleware do not execute

node.js version: v14.4.0

npm/yarn and version: 6.14.5

@koa/router version: 9.1.0

koa version: 2.13.0

Code sample:

router1.get('/user/query', async (ctx, next) => {
console.log('before')
})
router2.get('/:demo/query', async (ctx, next) => {
console.log('middle')
})
router3.get('/user/query', async (ctx, next) => {
console.log('after')
})

app.use(router1)
app.use(router2)
app.use(router3)

With koa-router v8.0.8, it work fine, it print
before
middle
after

But with koa-router v9.1.0, it print

before
middle

what should I do for the same result as v8.0.8

Missing src directory

Should the src directory be reconstructed as I doubt the lib folder was the src directory (then ignore the new src fodler from npm with .npmignore [and possibly ignore the lib directory from github?]), and maybe use typescript as the base and transpile it for npm, and provide the typedef for people using typescript?

Issue with dependency

node.js version: v12.16.3
@koa/router version: latest
koa version: latest

Hi!

Not sure if you seen this error before, but it seems that building with your latest causes this weird issue. Any advice on this would be helpful. Thank you!

Screen Shot 2020-06-01 at 12 14 20 PM

Current version(8.0.7) make Out Of Memory

node.js version: 10.16.0

npm/yarn and version: 1.17.3

@koa/router version: 8.0.7

koa version: 2.11.0

Error message:

<--- Last few GCs --->

[60877:0x102b00000]    35217 ms: Mark-sweep 1387.5 (1416.2) -> 1387.5 (1416.2) MB, 1598.4 / 0.0 ms  (average mu = 0.095, current mu = 0.000) last resort GC in old space requested
[60877:0x102b00000]    36649 ms: Mark-sweep 1387.5 (1416.2) -> 1387.5 (1416.2) MB, 1432.6 / 0.0 ms  (average mu = 0.058, current mu = 0.000) last resort GC in old space requested


<--- JS stacktrace --->

==== JS stack trace =========================================

    0: ExitFrame [pc: 0x3557fc5be3d]
Security context: 0x13a08dc1e6e9 <JSObject>
    1: /* anonymous */ [0x13a0aa29e261] [/Users/user/Documents/workspace/my-project/@koa/router/lib/router.js:~246] [pc=0x3558022e30f](this=0x13a0c2a0be31 <Router map = 0x13a0a2f5da09>)
    2: arguments adaptor frame: 25->0
    3: /* anonymous */(aka /* anonymous */) [0x13a0c24028d9] [0x13a0d3c026f1 <undefined>:30] [bytecode=0x13a00c60dcd...

FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory
 1: 0x10003cf99 node::Abort() [/Users/user/.nvm/versions/node/v10.16.0/bin/node]
 2: 0x10003d1a3 node::OnFatalError(char const*, char const*) [/Users/user/.nvm/versions/node/v10.16.0/bin/node]
 3: 0x1001b7835 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/Users/user/.nvm/versions/node/v10.16.0/bin/node]
 4: 0x100585682 v8::internal::Heap::FatalProcessOutOfMemory(char const*) [/Users/user/.nvm/versions/node/v10.16.0/bin/node]
 5: 0x10058eb84 v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationSpace, v8::internal::AllocationAlignment) [/Users/user/.nvm/versions/node/v10.16.0/bin/node]
 6: 0x10055de86 v8::internal::Factory::NewFixedArrayWithFiller(v8::internal::Heap::RootListIndex, int, v8::internal::Object*, v8::internal::PretenureFlag) [/Users/user/.nvm/versions/node/v10.16.0/bin/node]
 7: 0x1005052ae v8::internal::(anonymous namespace)::ElementsAccessorBase<v8::internal::(anonymous namespace)::FastPackedObjectElementsAccessor, v8::internal::(anonymous namespace)::ElementsKindTraits<(v8::internal::ElementsKind)2> >::GrowCapacity(v8::internal::Handle<v8::internal::JSObject>, unsigned int) [/Users/user/.nvm/versions/node/v10.16.0/bin/node]
 8: 0x1007a06bf v8::internal::Runtime_GrowArrayElements(int, v8::internal::Object**, v8::internal::Isolate*) [/Users/donghyun/.nvm/versions/node/v10.16.0/bin/node]
 9: 0x3557fc5be3d
10: 0x3558022e30f

Expected reason:

I tried to find the reason from the recent changes in PR(#50). I found some codes that expected bugs.

image

e07397c#diff-504fd89f344be1e9c4b583f3ebd7428aL262

In this refactoring, there are three i are declared and effected themself. I think it occurs infinite loop.

image

e07397c#diff-504fd89f344be1e9c4b583f3ebd7428aR283

Before refactoring, In this section(this[index] = cloneLayer) refered to index. But after refatoring it chaged like cloneRouter.stack[i] = cloneLayer.

I wonder where the index has been referenced and why I changed it to i.

cc.
@JacobMGEvans

Missing "params" for mostSpecificLayer in Router middlewares

koa-router version: 8.0.2

koa version: 2.8.1

Code sample:

See original implementation:
https://github.com/ZijianHe/koa-router/pull/490/files/da8ff9537049c3f062228591c4c54a3ca9284908

Half of it was implemented but not the storage of the already resolved params.

Expected Behavior:

ctx.params should be filled with the values of the current route.

Actual Behavior:

Route and possible route name is stored but it should also store the params extracted from it to process it on router-middleware level.

Typescript (Types)

Hi guys, I have experience with Node for building clients (front-end), now I am building an api with Koa. We know that now Typescript is everywhere then because this repository like so many others Node is so little support for typescript types. Is it not recommended to use Typescript for the backend with Node..? Will there be native type support in Koa, Thank You.

Add a TypeScript integration doc

Hi!

Can we have a TypeScript integration doc add to the project? I had a hard time trying to understand why my code was not compiling because of @types/koa-router vs @types/koa__router and this issue I found on StackOverflow.

I believe that other people had the same problem, and I saw on some issues that you don't really want to support TS and that's ok. But can I add a simple .md doc for TypeScript integration and reference it on the readme?

Thanks for the great package, you are awesome!

Router runs middleware on routes, which are not in array.

node.js version: v10.16.3

npm/yarn and version: 6.13.4

@koa/router version: 8.0.5

koa version: 2.11.0

Code sample:

const Router = require('@koa/router')
const validate = require('../middlewares/validator')
const guard = require('../middlewares/guard')

const AccountController = require('../controllers/account')

const router = new Router({
   prefix: '/account'
})

router.use(router.routes(), guard.ip.filter())

router.use([
   '/signin',
   '/update',
   '/password/change',
   '/password/forgot',
   '/password/reset',
   '/email/change/request',
   '/email/change/confirm',
   '/email/verify'
], validate())

router.use([
   '/signout',
   '/',
   '/update',
   '/password/change',
   '/email/change/request'
], guard.user.passport('jwt'), guard.csrf())

// Routes
router.post('/signin', guard.user.passport('local'), AccountController.signIn)
router.delete('/signout', AccountController.signOut)

router.get('/', AccountController.profile)
router.post('/update', AccountController.update)

router.post('/password/change', AccountController.password.change)
router.post('/password/forgot', AccountController.password.forgot)
router.post('/password/reset', AccountController.password.reset)

router.post('/email/change/request', AccountController.email.change.request)
router.post('/email/change/confirm', AccountController.email.change.confirm)
router.post('/email/verify', AccountController.email.verify)

module.exports = router

Expected Behavior:

Expected, that route '/' will effect exactly for route '/', when using:
router.use([ '/signout', '/', '/update', '/password/change', '/email/change/request' ], guard.user.passport('jwt'), guard.csrf())
So that passport middleware should run exactly on those routes, which are is this array.

Actual Behavior:

Unfortunately route '/' in the array of routes above effects on any other route, even on '/signin', which is not in array for passport middleware.

Additional steps, HTTP request details, or to reproduce the behavior or a test case:

Url params not part of ctx.request

Params are part of the request and should be part of the request object of the ctx in koa. Similar to how headers, query, url, method, and other request related data are. This request is more to follow the domain of where data should belong rather than fixing a bug. I have created, forked, updated, and unit tested the code.

Bad request (400) for route that is a copy of a successful route

Hi, I have a route module file for prefix /tasks with some routes. It has been working properly until I decided to include an extra route (GET /tasks/pending), for which I always get a 400 code result, even if I have it execute the same code of route /tasks/

The order in which I declare it doesn't change this weird behaviour. I can see it executes properly, because if I include console.log with the result of the query I can see the right result, but the client gets a "400 Bad response", even if the result of the query is present in the response body.

The same happened to me before with another route module. In that case I moved the route definition to another place in the file and I solved the problem (without understanding why). But in this case the problem persists

How can I check what's wrong?

Thanks

node.js version: v13.3.0

npm/yarn and version: 6.14.4

@koa/router version: 9.0.1

koa version: 2.12.0

Code sample:

const Router = require('koa-router');
let rows,result;

const router = new Router({
prefix: '/tasks'
});

router.get('/', async (ctx, next) => {
const query = "SELECT * FROM tareas order by grupo,inicio,previa";
[rows,result] = await ctx.dbPool.query(query);
ctx.body = rows;
next();
});

router.get('/pending', async (ctx, next) => {
const query = "SELECT * FROM tareas order by grupo,inicio,previa";
[rows,result] = await ctx.dbPool.query(query);
ctx.body = rows;
next();
});

router.get('/:period', async (ctx, next) => {

if (ctx.params.period == "m" ) {
    const mes = (new Date().getMonth()) +1 ; 
    const query = "SELECT * FROM tareas WHERE FIND_IN_SET(" + mes + ",Meses)>0 and Activa='S' order by grupo,inicio,previa";
    [rows,result] = await ctx.dbPool.query(query);
    ctx.body = rows;
} else {
    ctx.response.status = 400;
}

next();

});

... 4 more routes ...
...

Expected Behavior:

Actual Behavior:

Additional steps, HTTP request details, or to reproduce the behavior or a test case:

Documentation issue on npmjs.com

The documentation on npmjs.com still contains the line var Router = require('koa-router');, but should require '@koa/router'.

I could not figure out where this is actually coming from, as the API.md in the github repo contains the correct code. I've gotta admit I haven't pushlished to npmjs so far.

Would be nice to see this put right. (This might also be a good time to replace those vars)

post method not allowed

`const Koa = require('koa');
const Router = require('@koa/router')
const Boom = require('boom');

const app = new Koa();
const router = new Router();

router.get('/',ctx => {
console.log(ctx)
ctx.body = "Hello World";
});

router.get('/user/:id',ctx => {
ctx.body = Hello user ${ctx.params.id};
});

//no post method
router.post('/user',ctx => {
ctx.body = create a user;
})

app.use(require('koa-bodyparser')())
app.use(router.routes())
app.use(router.allowedMethods({
throw: true,
notImplemented: () => Boom.notImplemented(),
methodNotAllowed: () => Boom.methodNotAllowed()
}));

app.listen(3000)`

1.router has no post method
2.demo with boom needs update from 'new Boom.notImplemented()' to 'Boom.notImplemented()'

Develop branch?

Should we have a develop branch that feature branches get merged to, and use it to make future releases?

Issue with koa-session and ParameterizedContext

node.js version: v12.10.0

npm/yarn and version: 6.10.3

koa-router version: 8.0.5

@types/koa__router version: 8.0.2

koa-session version: 5.12.3

@types/koa-session version: 5.10.1

koa version: 2.11.0

Code sample:

TODO: @silenceisgolden - make repro

tsc --noEmit

Expected Behavior:

No errors in typescript compilation.

Actual Behavior:

src/controllers/session-controller.ts:26:7 - error TS2339: Property 'session' does not exist on type 'ParameterizedContext<any, RouterParamContext<any, {}>>'.

26   ctx.session.uid = user.id;
         ~~~~~~~

src/controllers/session-controller.ts:33:23 - error TS2339: Property 'session' does not exist on type 'ParameterizedContext<any, RouterParamContext<any, {}>>'.

33   const { uid } = ctx.session;
                         ~~~~~~~

src/utils/rotate-xsrf-middleware.ts:22:19 - error TS2339: Property 'session' does not exist on type 'ParameterizedContext<any, RouterParamContext<any, {}>>'.

22   const sid = ctx.session.uid;
                     ~~~~~~~

src/utils/validate-xsrf-middleware.ts:11:22 - error TS2339: Property 'session' does not exist on type 'ParameterizedContext<any, RouterParamContext<any, {}>>'.

11   const { id } = ctx.session;
                        ~~~~~~~

Additional steps, HTTP request details, or to reproduce the behavior or a test case:

Bench files not working properly (Improve Benchmark testing and usage)

  • nest() method seemed deprecated and was replaced by in the API so I used .middleware() seemed to handle the change ok.

  • I will create a separate PR with the benchmark code changes. 😃

  • Initial Fix Bench files & add script in package.json

  • Update Bench folder

  • Improve Bench server.js code (could still refactor and improve more)

  • Improve Makefile

  • Improve run shell file

  • Improve output, accuracy, and stability/consistency. (no tests on benchmark code server.js)

#51 is created, I am not sure but I think the server.js file can be improved for benchmark accuracy and stability/consistency.
Originally posted by @JacobMGEvans in #50 (comment)

Middlewares aren't called if there's no matched route

I found that this was also discussed in the old repository in ZijianHe/koa-router#182 ZijianHe/koa-router#257 ZijianHe/koa-router#239.

The problem can be seen with a code like this. The testcase could be made simpler but I left it like this so that it's closer to my real-world case, implementing CORS.

// @flow
const Koa = require('koa');
const Router = require('@koa/router');
const cors = require('@koa/cors');

function getRouter() {
  const router = new Router();

  router.get('/hello1', ctx => {
    ctx.body = 'world1';
  });
  router.use(cors());
  router.post('/hello2', ctx => {
    ctx.body = 'world2';
  });

  return router;
}

const app = new Koa();
const router = getRouter();
app.use(router.routes());
app.use(router.allowedMethods());

app.listen(20000);
console.log('server started on port 20000');

This is a curl command you can use to test this (this is a socalled "preflight CORS request"):

curl -i -H 'Origin: foo' -H 'Access-Control-Request-Method: POST' -X OPTIONS http://localhost:20000/hello2

The answer should be something like:

HTTP/1.1 204 No Content
Vary: Origin
Access-Control-Allow-Origin: foo
Access-Control-Allow-Methods: GET,HEAD,PUT,POST,DELETE,PATCH
Date: Fri, 10 Jan 2020 18:36:33 GMT
Connection: keep-alive

But instead it is:

HTTP/1.1 200 OK
Content-Type: text/plain; charset=utf-8
Content-Length: 0
Allow: POST
Date: Fri, 10 Jan 2020 18:36:28 GMT
Connection: keep-alive

This shows that this is the middleware of allowedMethods that runs, instead of the "cors" middleware.

Note this does work:

// @flow
const Koa = require('koa');
const Router = require('@koa/router');
const cors = require('@koa/cors');

function getRouter1() {
  const router = new Router();

  router.get('/hello1', ctx => {
    ctx.body = 'world1';
  });

  return router;
}

function getRouter2() {
  const router = new Router();

  router.post('/hello2', ctx => {
    ctx.body = 'world2';
  });

  return router;
}

const app = new Koa();
const router1 = getRouter1();
app.use(router1.routes());
app.use(router1.allowedMethods());

app.use(cors());

const router2 = getRouter2();
app.use(router2.routes());
app.use(router2.allowedMethods());

app.listen(20001);
console.log('server started on port 20001');

My main issue is that there's a surprise here: the router doesn't behave like koa and ignores all middleware if there's no matching route, and it's not documented anywhere. Koa instead will always run all middleware.

After various tries, I believe that this simple patch makes it work properly and doesn't have much impact:

diff --git a/lib/router.js b/lib/router.js
index a97941f..e681b6f 100644
--- a/lib/router.js
+++ b/lib/router.js
@@ -331,23 +331,24 @@ Router.prototype.routes = Router.prototype.middleware = function () {
     if (ctx.matched) {
       ctx.matched.push.apply(ctx.matched, matched.path);
     } else {
       ctx.matched = matched.path;
     }
 
     ctx.router = router;
 
-    if (!matched.route) return next();
-
     var matchedLayers = matched.pathAndMethod
-    var mostSpecificLayer = matchedLayers[matchedLayers.length - 1]
-    ctx._matchedRoute = mostSpecificLayer.path;
-    if (mostSpecificLayer.name) {
-      ctx._matchedRouteName = mostSpecificLayer.name;
+
+    if (matched.route) {
+      var mostSpecificLayer = matchedLayers[matchedLayers.length - 1]
+      ctx._matchedRoute = mostSpecificLayer.path;
+      if (mostSpecificLayer.name) {
+        ctx._matchedRouteName = mostSpecificLayer.name;
+      }
     }
 
     layerChain = matchedLayers.reduce(function(memo, layer) {
       memo.push(function(ctx, next) {
         ctx.captures = layer.captures(path, ctx.captures);
         ctx.params = layer.params(path, ctx.captures, ctx.params);
         ctx.routerName = layer.name;
         return next();

Please tell me if you'd like that I do a PR with some test changes, I can do it.

Using * wildcard no longer works

node.js version: 12.6.1

npm/yarn and version: npm 6.14.4

@koa/router version: 9.0.0

koa version: 2.11.0

Code sample:

const Koa = require('koa')
const Router = require('@koa/router')

const app = new Koa()
const router = new Router()
const port = 8000

router.get('*', (ctx) => {
  ctx.body = 'hello'
})

app.use(router.routes())

app.listen(port, () => {
  console.log(`listening on ${port}`)
})

Expected Behavior:

App should not crash

Actual Behavior:

App crashes:

/home/z/.tmp/1586900133/node_modules/path-to-regexp/dist/index.js:113
        throw new TypeError("Unexpected " + nextType + " at " + index + ", expected " + type);
        ^

TypeError: Unexpected MODIFIER at 0, expected END
    at mustConsume (/home/z/.tmp/1586900133/node_modules/path-to-regexp/dist/index.js:113:15)
    at parse (/home/z/.tmp/1586900133/node_modules/path-to-regexp/dist/index.js:172:9)
    at stringToRegexp (/home/z/.tmp/1586900133/node_modules/path-to-regexp/dist/index.js:329:27)
    at pathToRegexp (/home/z/.tmp/1586900133/node_modules/path-to-regexp/dist/index.js:403:12)
    at new Layer (/home/z/.tmp/1586900133/node_modules/@koa/router/lib/layer.js:48:17)
    at Router.register (/home/z/.tmp/1586900133/node_modules/@koa/router/lib/router.js:578:15)
    at Router.<computed> [as get] (/home/z/.tmp/1586900133/node_modules/@koa/router/lib/router.js:203:12)
    at Object.<anonymous> (/home/z/.tmp/1586900133/index.js:8:8)
    at Module._compile (internal/modules/cjs/loader.js:1147:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1167:10)

I believe the path-to-regexp upgrade in #71 broke this. The use case here is, for example, SSR on any route that doesn't match a previously defined one.

Localized routes

Hi!

I recently encountered a task where I needed my routes to be localized for different languages.
I also wanted a localized route only to be active for that language, i.e responding with a 404 if that route did not belong to that language.

I posted a question on StackOverflow which I then managed to solve myself: https://stackoverflow.com/questions/62081482/localized-routes-in-koa/

I am not sure if this is something that should be included in this package and if this is the best solution but it is a functionality that I missed and I suspect others might run into it as well.

If this is indeed something that should be included in this package maybe someone could coach me to write a proper pull request for such feature? :)

Unable to reuse nested routers (router mutation)

I wanted to reopen an issue that never got fixed in the old unmaintained koa-router.
ZijianHe/koa-router#244 (comment)

Tested with

koa-router version: 8.0.0

koa version: 2.7.0

Code sample:

sharedRouter.get('/hello', (ctx) => {
    ctx.body = 'Hello World!';
});

rootRouter.use(sharedRouter.routes());
rootRouter.use('/foo', sharedRouter.routes());
rootRouter.use('/bar', sharedRouter.routes());

app.use(rootRouter.routes());

Expected Behavior:

The following paths should return a Hello World! body:

  • GET /hello
  • GET /foo/hello
  • GET /bar/hello

Actual Behavior:

Each route returns a 404, as koa is unable to match a route.

Each time the shared router is mounted (.routes() is called), the shared router is mutated, with the new mounted path being prefixed to the shared router. The resulting route for the shared router is /bar/foo/hello, which I believe is then tested three times.

Use case

While this can easily be avoided, it is an inconvenience and unpredictable behaviour.

A mutation is not predictable. My understanding is that routers should behave like self-contained packages that can be mounted dynamically in several places, providing the same route tree.

npm project to use

Hi,
Normally this project had been published in @koa/router in npm but i have seen that 3 days ago this exactly project has been also published in koa-router that is the deprecated project... witch npm project should we use as dependency?

Best Regards

Routing does not handle optional route parameter with prefix

node.js version:
13.3.0

npm/yarn and version:
npm 6.4.1
yarn 1.19.2 (this is what i'm using)

@koa/router version:
8.0.5

koa version:
2.11.0

Code sample:

const router = require('@koa/router')()
const Koa = require('koa')
// following line included to demonstrate difference in underlying library - version 6.1.0
const { pathToRegexp } = require("path-to-regexp")
const app = module.exports = new Koa()
const middleware = async (ctx, next) => { ctx.body = 'it worked'; return await next() }

const withoutIDRoute = '/foo{.:ext}?'
router.get('withoutID', withoutIDRoute, middleware)

app.use(router.routes()).use(router.allowedMethods())

console.log(pathToRegexp(withoutIDRoute).test('/foo.json')) // outputs true
console.log(pathToRegexp(withoutIDRoute).test('/foo')) // outputs true

app.listen(3080)

Expected Behavior:

  • http://localhost:3080/foo.json returns it worked as a text response
  • http://localhost:3080/foo returns it worked as a text response
  • More generally, @koa/router matches routes that path-to-regexp matches (as documented in this section)

Actual Behavior:

Both of the aforementioned URLs result in a 404 Not Found response

Middleware used in a nested router is evaluated on unrelated routes.

Descriptions

The middleware used in a nested-router can be triggered if the regexp matches (matchedRoute) with the unrelated path. The behavior is something intuitive or not easily predictable.

I wrote one router like this, intending to run "checkAuth" middleware when the user accesses to /a/1 or any other subroutes written under /a.

function createAjaxRouter() {
  const router = createRouter('/a');
  router.use(checkAuth);
  router.get('/1', routeHandler(1))
  return router.routes();
}

However, accessing /about (defined elsewhere) can trigger "checkAuth". This is because middleware is evaluated when the request-path matches /a(.*).

This issue can be avoided by defining the routes /about before this nested router, but manually managing the order of nested routes is difficult.

I wonder if the library can guarantee that middleware used inside the nested-routes are used only in nested routes.

Environment

node.js version: v11.6.0 (Windows)
npm/yarn and version: 6.14.5
@koa/router version: 9.0.1
koa version: 2.12.0

Code sample:

const Koa = require('koa');
const Router = require('@koa/router');

function createRouter(prefix) {
  return new Router({
    prefix
  });
}

function routeHandler(x) { 
  return async ctx => {
    console.log(x);
    ctx.body = `${x}`;
  }
}

async function checkAuth(ctx, next) {
  console.log('checkAuth');
  // if (ctx.query['auth'] != "1") {
  //   throw new Error("not authed");
  // }
  await next();
}

function createAjaxRouter() {
  // match for '/a(.*)'
  const router = createRouter('/a');
  router.use(checkAuth);
  router.get('/1', routeHandler(1))
  return router.routes();
}

function createIndexRouter() {
  // match for '(.*)'
  const router = createRouter();
  router.get('/', routeHandler('index'));
  router.get('/about', routeHandler('about'));
  return router.routes();
}

function setRouter(app) {
  // match for '(.*)'
  const router = createRouter();
  router.use(createAjaxRouter());
  router.use(createIndexRouter());

  // order of path in routes.router.stack =>
  // ['/a(.*)', '/a/1', '/', '/about']
  const routes = router.routes();
  app.use(router.routes());
  app.use(router.allowedMethods());
}

const app = new Koa();
setRouter(app);
app.listen(3010); 

Gist

Expected Behavior:

Access to /about should not run the middleware used for nested routes with prefix: /a

Actual Behavior:

Access to /about can trigger the middleware defined in the nested routes.

Router GET with parameter and Nested Routes It's possible?

Hi, have a problem with the routes, it is the following:

All courses: Works

/courses

Specific course: Works

/courses/1

Specific course with Nested Routes: Doesn't work - 404

/courses/1/levels

courses.js

const levels = require('./levels');

const route = new koaRouter({ 
    prefix: '/courses' 
});

route.get('/', koaBody(), async (context) => {
    // Some Logic
    context.body = { "Courses": "All" };
});

route.get('/:id', koaBody(), async (context) => {
    // Some Logic
    context.body = { "Course": "1" };
});

route.get('/:id/levels', levels.routes());

levels.js

const route = new koaRouter({ 
    prefix: '/levels' 
});

route.get('/', koaBody(), async (context) => {
    // Some Logic
    context.body = { "Courses Levels": "All" };
});

Can anybody help me?

Multiple routes are executed

There is an open bug in the original koa-router repository which I can still reproduce: ZijianHe/koa-router#231

I think this behaviour makes sense in a way, but is there any solution to set routes to be "exclusive"?

Code sample

const Koa = require('koa');
const Router = require('@koa/router');

const app = new Koa();
const router = new Router();

router.get('/users/all', (ctx, next) => {
  console.log('route 1');
  ctx.body = 'route 1';
  next();
});

router.get('/users/:id(.*)', (ctx, next) => {
  console.log('route 2');
  ctx.body = 'route 2';
  next();
});

app.use((ctx, next) => {
  console.log(ctx.request.method, ctx.request.url, 'START');
  next();
});

app
  .use(router.routes())
  .use(router.allowedMethods());

app.use((ctx, next) => {
  console.log(ctx.request.method, ctx.request.url, 'END');
  next();
});

app.listen(3333);

Expected Behaviour

$ node app
GET /users/all START
route 1
GET /users/all END

Actual Behaviour

$ node app
GET /users/all START
route 1
route 2
GET /users/all END

Versions

@koa/router: 9.1.0
koa: 2.13.0
node: 12.13.1
npm: 6.12.1

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.