Adding OpenAPI Support to Mummy: Lessons from Extending Nim's Thread-Per-Request HTTP Server
Mummy is a multi-threaded HTTP 1.1 and WebSocket server for Nim built around a simple, unapologetic idea: skip async/await entirely and let a pool of worker threads handle requests the old-fashioned way — one thread, one request, blocking calls allowed. No Future[T], no colored functions, no async stack traces to untangle. For a systems language with real threads and ARC/ORC to make that cheap, it's a compelling trade.
What Mummy doesn't ship with, though, is any notion of API documentation. There's no built-in way to describe your routes, generate a schema, or hand a frontend team (or your future self) a /docs page. That gap is what got me building.
Why This Gap Matters
Nim's web ecosystem is small enough that most frameworks are optimized for the author's own use case first. Mummy is optimized for raw throughput and simplicity — and it succeeds at both. But once an API has more than a couple of routes, "read the source to find out what a request body looks like" stops scaling. OpenAPI (formerly Swagger) solves this by generating a machine-readable schema of your API directly from your code, which then powers interactive docs, client SDK generation, and request validation.
I forked Mummy and built this in as a layer on top, rather than a separate tool that has to be kept in sync by hand.
What I Built
The fork adds two core modules — openapi_schema.nim and openapi_router.nim — plus an OpenApiRouter that wraps Mummy's normal routing with metadata: summaries, tags, and response schemas generated via schemaOf. Every one of the 12 bundled examples now serves a live /openapi.json and an interactive /docs page, generated from the same route definitions that handle requests — no separate spec file to maintain.
From there I added three more pieces that came up naturally once the schema layer existed:
- Typed path params (
openapi_params.nim) — pathParam[T](request, "id") pulls a path segment straight out as an int, string, or whatever type you declare, instead of manually parsing strings on every handler.
- Request body validation (
openapi_validation.nim) — parseValidatedBody[T] checks an incoming JSON body against the same schema that schemaOf generated for your docs, so the validation and the documentation can't drift apart.
- Composable middleware (
openapi_middleware.nim) — a Middleware type and a use() method on the router, with logging and bearer-auth middleware included out of the box.
Every feature was compiled and exercised with real HTTP requests, not just nim check, before I called it done — including converting a wildcard WebSocket route to a named path param and confirming it fixed a small latent bug where the old code read the raw request URI (query string and all) instead of the decoded path.
Reading through discussion around Mummy on the Nim mailing list and forum was useful context while building this. One recurring theme: Mummy's design intentionally keeps some things out of scope in the name of simplicity. A developer comparing thread-per-request servers noted that Mummy's multipart handling, while present, buffers the entire upload into memory rather than streaming it — a deliberate trade-off for simplicity that matters if you're building something like a file-upload service, and pointed to alternatives like Guildenstern for cases needing streaming multipart support.
That trade-off is directly relevant to schema-driven validation work: parseValidatedBody[T] inherits the same "load it all, then check it" model. For typical JSON APIs that's a non-issue, but it's a useful thing to know going in if you're planning a Mummy-based service that also needs large file uploads — you may want to keep that path outside the validated-body flow rather than fight the framework's grain.
It's also worth knowing you're not alone in wanting typed, schema-aware APIs on Mummy — a separate project, Sarcophagus, tackles a similar problem from a different angle: typed request/response encoding across JSON, CBOR, and MessagePack, plus its own Swagger/OpenAPI generation and OAuth2 helpers. Different design choices, same underlying itch in the ecosystem — which suggests this is a real gap worth filling rather than a one-off preference of mine.
Putting It to Use
To validate the fork beyond the bundled examples, I built a small Todo API demo using the typed params, validation, and middleware together, and I'm deploying it live on Render as a working reference — a REST API with real request validation and generated docs, running on a threaded model with no async in sight.
Where This Goes Next
The fork is usable today for anyone building JSON APIs on Mummy who wants documentation and validation that can't silently drift from the actual routes. If you're working in the Nim web space and hit the same "no schema, no docs" wall, I'd genuinely like to hear how you're solving it — feel free to reach out.