Juggling client contracts and building full-stack applications means spinning up new backends constantly. But manually wiring up Express routers, configuring JWT authentication, and setting up Zod validation for every single project was completely destroying my momentum.
I wanted the seamless developer experience of Next.js, but for a standalone Node API. Since nothing quite fit the bill, I built bro.js.
It is a zero-boilerplate engine wrapper around Express that handles the tedious infrastructure out of the box, letting you focus entirely on your logic.
-
File-Based Routing: Just drop
[id].get.jsinto your routes folder, and the endpoint is instantly live. - Built-in Zod Validation: Pass a schema to the route definition, and the framework automatically rejects bad requests with a 400 error.
-
Native WebSockets: Socket.io is pre-configured and injected directly into your route context (
ctx.io). -
Auto-Generated SDKs: Run
bro sdk, and it compiles a typed client SDK perfectly formatted for your frontend.
You can boot a new environment instantly right from your terminal:
npx create-bro-framework@latest my-api
I would love for the community to try it out and roast the code. You can check out the documentation here or drop a star on the GitHub Repo.
Top comments (2)
The momentum argument is real — I keep rebuilding the same Express wiring on internal tooling and it's a half-day every time.
Two things the post doesn't cover that decide whether I'd adopt it: the SDK generation, and WebSockets at more than one instance. On
bro sdk— is the typed client generated from the file tree plus the Zod schemas at build time, and what happens to the contract when a route file is renamed? We generate clients from an OpenAPI spec and the painful part isn't the generator, it's keeping the spec honest; if bro derives it from route definitions I'd want to know how a stale client gets caught.On Socket.io injected into
ctx.io: how do you handle session affinity when the app runs behind a proxy? That's what bit us on a previous project — it worked until it needed a second instance, then reconnects started landing on the wrong node and we added a Redis adapter anyway, which brought back the setup overhead the framework was meant to remove.Appreciate the sharp feedback 🫶, these are real production pain points:
bro sdk & Stale Contracts
The SDK statically derives types directly from your routes/ files and Zod schemas, removing OpenAPI spec drift entirely. When you rename or remove a route file and re-run bro sdk, the old method signature is removed from the generated client. Your frontend's TypeScript compiler (tsc) will immediately flag the broken calls at build time.
WebSockets & Multi-Instance (ctx.io)
ctx.io is intentionally scoped for single-instance simplicity to avoid broker overhead early on. Once you scale past one node behind a proxy, sticky sessions and a pub/sub layer like Redis are inevitable. Currently, that requires plugging in @socket.io/redis-adapter manually. The planned fix on the roadmap is zero-config: dropping a redis URL into bro.config.js that attaches the adapter under the hood automatically. So i'll work on that and keep you updated!
Would love your thoughts if you take it for a spin on any internal tools!