Skip to content

Improvements to Shelf Router #474

@namzug16

Description

@namzug16

Improvements to Shelf Router

Based on this comment I decided to play around and implement a router based on radix tree similar to how echo does it

From there, I went down into the rabbit hole and decided to add more features like grouping and middlewares

The final result (in use) looks like this

void main() async {
  final router = Router.builder()
    ..use(logRequests())
    ..use((Handler handler) => (Request request) {
          print('Global middleware: ${request.method} ${request.requestedUri}');
          return handler(request);
        })
    ..get('/', (Request req) => Response.ok('Hello from the root route'))
    ..get('/hello', (Request req) => Response.ok('Hello World!'))
    ..get('/user/<name>', (Request req, String name) => Response.ok('Hello, $name!'))
    ..post('/data', (Request req) async {
      final payload = await req.readAsString();
      return Response.ok('Data received: $payload');
    });

  router.group('/group1')
    ..get('/info', (Request req) => Response.ok('Group1 Info'))
    ..get('/items', (Request req) => Response.ok('Group1 Items list'))
    ..put('/item/<id>', (Request req, String id) => Response.ok('Updated item with id $id'))
    ..delete('/item/<id>', (Request req, String id) => Response.ok('Deleted item with id $id'));

  router.group('/group2')
    ..use((Handler handler) => (Request req) {
          print('Group2 middleware 1: ${req.method} ${req.requestedUri}');
          return handler(req);
        })
    ..get('/status', (Request req) => Response.ok('Status OK'))
    ..use((Handler handler) => (Request req) {
          print('Group2 middleware 2: ${req.method} ${req.requestedUri}');
          return handler(req);
        })
    ..get('/metrics', (Request req) => Response.ok('Metrics: 123'));

  final server = await serve(router.call, 'localhost', 8080);

  print('Server listening on port ${server.port}');
}

The tree created from this router looks something like this

Middlewares will be inherited down the tree

flowchart TD
    %% Radix Tree Root and HTTP Verb Layer
    R0["Root"]
    R0 --> GM(["Global Middleware"])
    GM --> GET["GET"]
    GM --> POST["POST"]
    GM --> PUT["PUT"]
    GM --> DELETE["DELETE"]

    %% GET Method Routes
    GET --> R1["/"]
    GET --> R2["/hello"]
    GET --> R3["/user"]
    R3 --> R4["/&lt;name&gt;"]

    %% Group1 Routes (prefix: /group1)
    GET --> G1["/group1"]
    G1 --> G1a["/info"]
    G1 --> G1b["/items"]

    %% Group2 Routes (prefix: /group2)
    GET --> G2["/group2"]
    G2 --> G2M1(["Middleware 1"])
    G2M1 --> G2a["/status"]
    G2M1 --> G2M2(["Middleware 2"])
    G2M2 --> G2b["/metrics"]

    %% POST Method Routes
    POST --> P1["/data"]

    %% PUT Method Routes (Group1 Route: /group1/item/&lt;id&gt;)
    PUT --> G1_put["/group1"]
    G1_put --> G1_put_item["/item"]
    G1_put_item --> G1_put_id["/&lt;id&gt;"]

    %% DELETE Method Routes (Group1 Route: /group1/item/&lt;id&gt;)
    DELETE --> G1_del["/group1"]
    G1_del --> G1_del_item["/item"]
    G1_del_item --> G1_del_id["/&lt;id&gt;"]
Loading

I think this kind of router would add value to the ecosystem. There are third party alternatives that achieve something similar, but I would prefer to have a native solution rather than using/creating an external library

Would you guys be open to a PR?

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions