Bandit was written specifically with the goal of serving Plug / WebSock and nothing more (it’s literally the very first line of the project README :). The reason for this is that at some point, you need to come up with some sort of abstraction to decouple your business logic from the underlying HTTP server logic, and Plug is about as simple of an abstraction as is possible to still be useful in practice. Cowboy’s underlying abstraction (‘handlers’) aren’t somehow any more magical than Plug; it’s still just a codified abstraction between an HTTP server and your business logic. Why José went with plug_cowboy instead of just building directly on top of Cowboy’s handler behaviour is a question for him, but my guess is that handler is actually a fairly awkward API to work with. plug_cowboy actually did (and does) a great job of smoothing that API out to something more usable in the form of Plug’s single call/2 based structure.
This isn’t to say that Plug is perfect, far from it. In particular, its pattern of ‘everything happens within a single function call’ means that it’s very awkward to do anything not directly related to servicing the request on the request process, at least not without involving other processes. Things like gRPC or implementing an event-aware MCP server are either not possible or else require some delegate coordination with another process. This isn’t entirely bad, or even an anti-pattern in the OTP world, but it does feel awkward in use at times.
Nor is WebSock perfect. Its spec came from extracting the existing (Cowboy-based) Websocket behaviour from Phoenix, and as a consequence is only able to send messages to the client as a return value from one of the handle_* callbacks; there’s no way to proactively send a message to the client.
My original design goal with Bandit was to very much intentionally lean on Plug as a simple abstraction, and to build the simplest thing that could turn HTTP requests into Plug calls. Every line of code in the project is laser focused on this goal; the fact that there is no lower level abstraction is kinda the whole point.
I guess my question back to you would be: if not Plug, then what?


















