Twice Failed (HTTP) Negotiations
So, I've been wanting to re-do my site for a while now, and, like the good developer I am, I've been writing micro-frameworks instead, because the other 2000 didn't strike my fancy.
I Gave myself the following goals:
- Small, stupidly small
- Pluggable, stupidly pluggable
And set off.
First Negotiation: MiddleStack
The neat thing about middlewares, is that they can edit whats going in, whats going out, and have full control over what happens next. Sounds perfect to build an entire framework around!
As seen in the README
:
import {route, middlestack} from "https://deno.land/x/middlestack/mod.ts";;
Deno.serve(middlestack([
route('GET', '/', async (req: Request) => {
return new Response("Hello world!")
}),
]));
With route
being a middleware factory. set_header
and error_handler
middlewares also available out of the box.
This is all fine and stuff, but there was a few problems:
- I lost friends for having
next => req => {}
be the signature instead of(req, next) => {}
- I lost myself for not completely embracing HTTP
HTTP has a lot of neat things, for example, OPTIONS
to find out what you can do to a specific URL, or HEAD
just to get the headers. To be able to support both, I'd need to know all possible pairs of URL
s and methods. Not easy! So my goals changed:
- Don't care about pluggability so much
- Handle routing all as one singular thing
Second Negotiation: Cruet
An absolute abomination of a code-base, but it gets the job done, somehow.
With this new codebase, you just provide a folder of routes, even stuff like /post/:id(\d+).ts
, and those files will automatically have their functions read, and pulled into a routing function, dynamically loaded too, to reduce load times on Deno Deploy et al.
That might sound scary, and according to votes, you'd be 50% right! But as a benefit of this complicated pile of logic, HEAD
is automatically generated from a GET
, and OPTIONS
is also automatically handled!
Negotiations to Come
But I felt like I was forgetting something, so I had a good long think, and then remembered, Content negotiation. Something I locked away deep in the recesses of my mind because of needing some mild thinking, but also because there is so many options.
Accept
, which allows specifying desired file types, Accept-Language
which is the same for languages, Device-Memory
, Viewport-Width
, Width
, But then if people have incorrectly configured devices, you need to ignore Accept-Language
as well.
Even if I add support in my router for all these options, on the server side, things arn't so great.
Dealing with json
and html
is easy, return json
if json
, pass through a template if you need html
, but that is only 2 of the types, and my framework doesn't have any in-built templating. No MVC concepts here, just routes and only routes.
And then languages... I mean, I only publish in one (at the moment), but there is a strong desire to just have support for the feature regardless. 3 methods × 1.5 file types × 2 languages, we already end up with 9 routing entries for a single URL.
Perhaps a rich HTTP server cannot coexist with a simple framework. Perhaps there are solutions to join the two concepts. But that day is not today, I'm tired.