lukeed / trouter

:fish: A fast, small-but-mighty, familiar fish...errr, router*
MIT License
634 stars 23 forks source link

how to remove route dynamically? #13

Closed neuronetio closed 4 years ago

neuronetio commented 4 years ago

in polka you can add routes dynamically after your app is running (after listen method) but I don't see any way to unregister a route?

why? for example you have a system where user is creating custom categories and subcategories (tree like structres) and you want to add routing for this categories without need to restart whole app (other users may use this system right now)

or when your system has a plugins that should be installed/removed dynamically like prestashop or wordpress with custom pages (without restarting whole app because a lot of other users are using this system right now) how to do it?

lukeed commented 4 years ago

Hey, I don't include an off method because that's a dangerous pattern IMO. Servers should have static behavior, and at the very least, not lose behavior.

If you still want to do this, you can yourself pretty easily. You just have to undo this line: https://github.com/lukeed/trouter/blob/master/index.mjs#L29

const Trouter = require('trouter');

class Router extends Trouter {
  remove(method, pattern, handler) {
    let i=0, tmp, idx;
    for (; i < this.routes.length; i++) {
      tmp = this.routes[i];
      if (tmp.method !== method || tmp.pattern !== pattern) continue;
      idx = tmp.handlers.indexOf(handler);
      if (index === -1) continue;

      // remove everything if this was the only handler
      // otherwise just remove the single handler
      if (tmp.handlers.length === 1) {
        this.routes.splice(i, 1);
      } else {
        this.routes[i].handlers.splice(idx, 1);
      }
    }

    return this;
  }
}

Hope that helps!

neuronetio commented 4 years ago

thanks a lot! with small modification I've got it working!

add(method, path, ...fns) {
        let { keys, pattern } = parse(path);
        this.routes.push({
            keys,
            pattern,
            path: path.toString(),
            method,
            handlers: fns.flat()
        });
        return this;
    }

    remove(method, path, handler) {
        let i = 0,
            currentRoute,
            idx,
            routes = this.routes;
        path = path.toString();
        for (const len = routes.length; i < len; i++) {
            currentRoute = routes[i];
            if (currentRoute.method !== method || currentRoute.path !== path)
                continue;
            idx = currentRoute.handlers.indexOf(handler);
            if (idx === -1) continue;
            // remove everything if this was the only handler
            // otherwise just remove the single handler
            if (currentRoute.handlers.length === 1) {
                this.routes.splice(i, 1);
            } else {
                routes[i].handlers.splice(idx, 1);
            }
        }
        return this;
    }

thanks!

lukeed commented 4 years ago

Cool!

Although, I don't think you'd want to target by "path" – if I understand correctly, that would be the incoming request path? That'd be different nearly every time & wouldn't match anything. I used pattern so that you, the server-owner, can reuse the same pattern you've already used to add the route.

neuronetio commented 4 years ago

no no, it is ok, I want to add /hello/:world and later remove /hello/:world too - incoming requests doesn't matter for me but thanks!

lukeed commented 4 years ago

Gotcha, then yeah, relying on the pattern will work for you.

Good luck!