Skip to content

Improving the ergnomics of the httpServerHandler function #4734

@jasnell

Description

@jasnell

/cc @anonrig @cloudflare/workers-runtime-nodejs @kentonv

The experimental httpServerHandler function takes, as input, a second "handlers" option whose own properties get assigned to a new object.

return Object.assign(handlers, {
  //...

Currently it would be called like:

const handlers = {
  scheduled(...) {...}
};
const handler = httpServerHandler({ port: 123 }, handlers);

// unfortunately, handler !== handlers because handler is
// a new object with functions copied from handlers.

The intent here is to allow the handler to have other handlers attached to it.

Unfortunately, the ergonomics here are a bit awkward as this method does not work when one wants to use the WorkerEntrypoint or export a class, as the tests illustrate by including the following example:

export class GlobalService extends WorkerEntrypoint {}
Object.assign(GlobalService.prototype, httpServerHandler({ port: 9090 }));
// Creates an object in httpServerHandler then copies those to the GlobalService prototype

Some of the potential problems that arise from this occur when the handlers object passed into httpServerHandler is an instance of a class, e.g. class Foo { #a: 1; scheduled() { /* use this.#a */ }}; export default httpServerHandler({ ... }, new Foo());, which would end up not working as expected. We can document this as a limitation but it's possible to get better ergonomics here.

It would be better, I believe, to be able to pass either a class constructor into the httpServerHandler or an instance object and have the fetch handler assigned... so something like:

class MyHandler extends WorkerEntrypoint {};

// httpServerHandler would modify the MyHandler.prototype and return MyHandler
const handler = httpServerHandler({ ... }, MyHandler);
strictEqual(handler, MyHandler);
strictEqual(typeof handler.prototype.fetch, 'function');

and

const myHandler = {
  scheduled(...) { ... },
};

// httpServerHandler would assig the fetch handler to
// myHandler and return it, rather than creating a new
// object.
const handler = httpServerHandler({ ... }, myHandler);
ok(hander === myHandler);  // same instance
strictEqual(typeof handler.fetch, 'function');

This change would require a compat flag if we shipped the current version so I'd like to consider this change being made before lifting the experimental compat flag.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions