Marathon

A framework and collection of packages for writing HTTP servers, built on top of the shelf package.

Base

The "base" or skeleton of this library should expose an API by which both first-party and third-party can hook into the server via middleware or handlers. An example of important middleware might be session management or authenticators; given the dynamic nature of these tasks, we can't possibly encapsulate every possibility in this library.

This API should integrate cleanly with Shelf. Shelf will handle the "dirty work" of actually receiving and responding to HTTP requests; rewriting Shelf's functionality in this library is transparently useless since Google officially maintains that library and it's relatively stable.

The base should also feature routing. Since the majority of people writing an HTTP server need some form of routing, it's reasonable to bundle it in the main library. Routing should use shelf_router underneath, for much the same reasons listed above for the main shelf package. However, we do have to overlay shelf_router with our own syntax to allow us a space to define hooks.

An example syntax, using only the base package, might look like this:

// in main.dart
import "package:marathon/marathon.dart";
import "service.dart";

void main (List<String> args) async {
  // The Router class is defined in shelf_router.
  var router = Router(); 
  
  // Extension method from Marathon, that adds routes from EchoService to shelf_router.
  router.register(EchoService()); 
  
  await serve(router.handler, 'localhost', 8080); // defined in shelf_io
}

In this example, we handle requests made to localhost on port 8080. If the request is a get or post request to "/echo/<somestringhere>", we return an OK Response containing the message "echo <somestringhere>".

Now, suppose we wanted to add some Middleware that mutated the double request and passed a doubled name in:

This is fine for individual Handlers, but the syntax is repetitive and verbose when used across every handler in a RouteGroup. If every handler in a RouteGroup uses the same logic, we can do something like:

This can wrap every route in a group with Middleware. This is a powerful tool, but also somewhat easy to abuse. Regardless, the provided functionality is immensely useful for plugging into Marathon, since the vast majority of plugins will be Middleware in some way.

Last updated

Was this helpful?