Declare intent. Derive execution. Build your product.
A zen, opinionated, type-safe TypeScript framework that turns what your code needs into a validated execution graph. Green Tea derives the order, runs only the dependencies each route actually requires, and shows you exactly what it built.
You focus on business logic. Green Tea maintains the foundation.
Beta · Node · Deno · Bun · Edge
Ownership
Who should do the infrastructure work?
- Execution order
- Green Tea
- Dependency validation
- Green Tea
- Route-specific pipeline
- Green Tea
- Missing dependency checks
- Green Tea
- Streaming mechanics
- Green Tea
- OpenAPI projection
- Green Tea
- Secure transport defaults
- Green Tea
- Runtime serving boundary
- Green Tea
- Business rules
- You
The framework should carry framework complexity. Your application should carry product complexity.
The idea
Declare what your code needs. Not when it should run.
A handler needs user. Authorization needs user. User comes from authentication. Authentication needs a session. That is already enough information to describe execution order — so Green Tea builds the graph from it, validates the wiring at boot, and runs only the slice the route requires.
session → auth → user → authorization →permissions → handler
- Need an ACL check on a user? The ACL is a step that itself needs the user. Ask for the ACL — the user is pulled in for you.
- Add a
friendsprovider, then ask forfriends— and the user arrives with the friend list.
You describe the application; the framework derives the pipeline. DI frameworks resolve a graph of services — Green Tea resolves your request pipeline the same way, from what each route asks for rather than from the order you happened to write things in.
Route slicing
Run what the route needs. Not everything registered around it.
GET /pricesmarkethandlerA public endpoint never asks for a user, so authentication is not in its graph slice.
GET /portfoliosessionauthuserportfoliohandlerA protected endpoint needs the user, so Green Tea follows the graph and includes it.
Routes don't inherit infrastructure just because it was registered nearby. Execution follows dependency demand.
See it
The signature is the contract.
If your handler needs it, it says so. Green Tea knows what the handler requires, which steps produce those values, and what those steps need in turn — and if the graph can't satisfy that contract, it fails before serving traffic. No hoping auth ran first, no archaeology through app.use().
import { createApp, Provider, Step, Route, Get, Module, needs } from '@green-tea/core';
@Provider({ provides: 'db' })
class Database {
provide() { return { db: connect() }; }
}
@Step({ provides: 'user', needs: ['db', 'req'] })
class Authenticate {
run(ctx) { return { user: ctx.db.find(ctx.req.headers['x-token']) }; }
}
@Route('/me')
class MeController {
@Get('/')
me(@needs('user') user) {
return user; // ask for `user` — the graph wires db + auth for you
}
}Then make the framework show its work: app.explain() prints the ordered pipeline, the live graph is served by your own app, and the same metadata projects into OpenAPI. Automated does not have to mean invisible.
Real-time
One mental model. Different transports.
Your business code has one idea — values arriving over time — and in Green Tea that idea is an AsyncIterable. Declare the transport with a decorator (@Sse, @Ws, @Stream) and return the stream; the framework handles framing, backpressure, cleanup and disconnects. Only that one line changes between them, and your wire contract is what you declared — never a surprise sprung by a return value.
AsyncIterable → SSE · NDJSON · WebSocket
Foundation ownership
We maintain the foundation. You maintain the product.
Routing, graph execution, validation integration, streaming, security defaults, errors, OpenAPI, lifecycle, runtime adapters — the framework work your application would otherwise assemble and then keep working. The goal isn't fewer dependencies for a badge; it's fewer moving parts your team has to understand, upgrade and debug.
Derive execution
Green Tea topologically sorts the graph. Stop maintaining execution order by hand.
Fail before traffic
A missing dependency or an ambiguous route fails loudly at boot, not in production.
Real validation
Standard Schema; bring zod, valibot, or arktype. No validator dependency in core.
See what was derived
Print the ordered pipeline, open the live graph, project the same metadata into OpenAPI 3.1.
Secure by default
Hardened response headers and CORS; native TLS on Node.
A small foundation
reflect-metadata is the only runtime dependency; ws and busboy are optional peers for sockets and uploads.
Portability
Your application model shouldn't belong to a runtime.
The graph, routes, validation and business logic stay the same on Node, Deno, Bun and Cloudflare Workers. Runtime-specific serving is isolated at the transport boundary — the only line that changes is how the application is served.
app.listen(port)serveDeno(app)serveBun(app)edgeHandler(app)
Your product code shouldn't have to care which runtime won this year. (The edge has no filesystem or mesh, so those features stay on the server runtimes.)
How it compares
Not another framework. A different model.
Every framework here can serve /users/:id. The difference shows up as the app grows — more dependencies, real-time, more than one machine.
| Capability | Express | Fastify | NestJS | Green Tea. |
|---|---|---|---|---|
| The pipeline is | a positional chain | hooks + plugins | DI + guards/interceptors | a graph, topologically sorted |
| Order comes from | the line you wrote it on | hook phase + registration | module wiring | what each step needs |
| Is req.user there? | hope so | hope so | if the guard ran | boot fails if nothing provides it |
| See the pipeline | read the code | read the code | paid Devtools | explain / graph, free |
| Real-time | bolt on a ws/sse lib | a plugin | a separate gateway | one AsyncIterable primitive |
| Runs on | Node | Node | Node | Node · Deno · Bun · edge |
| Runtime deps | minimal | minimal | heavy (rxjs, …) | reflect-metadata only |
Same idea, said plainly: other stacks answer “what does this handler depend on?”, “how do I push data over time?” and “how do I call another service?” with three subsystems. Green Tea answers all three with one — the graph.
Straight answers
Questions worth asking about a beta.
Is Green Tea ready for production?
It's beta, on the RC track — the API can still shift between releases. It fits greenfield services, internal tools, and side projects where you own the deploy and can pin versions. If you need a decade of third-party middleware today, stay on Express for now.
Can I drop it into my existing Express app?
No — and that's the point. Green Tea replaces the middleware chain with a dependency graph, so adopting it means rewriting the pipeline, not swapping a router. It shines on a new service or a bounded rewrite, not as an incremental Express patch.
How does it perform against Express or Fastify?
Performance isn't the pitch — the model is — but there's a reproducible benchmark in the core repo (BENCHMARKS.md), written to be hard on us, not flattering. On a single-box harness green-tea keeps pace with Fastify and runs several times faster than Express, behind only raw Node http. Read the caveats there: it's loopback on one machine, so trust the ratios between frameworks, not the absolute numbers. The graph is topologically sorted once at boot, not per request — you don't pay for it on the hot path.
What will it add to my dependency tree?
One runtime dependency: reflect-metadata. ws and busboy are optional peers you install only if you use WebSockets or uploads. Validation is bring-your-own via Standard Schema (zod, valibot, or arktype), so the core stays lean.
Is mesh (multi-service) ready to use?
Not yet — mesh is alpha and gated behind an explicit opt-in. Build on the single-service core for now, and treat mesh as a preview, not a foundation.
What does "beta" mean for breaking changes?
Expect occasional breaking changes on the RC track, called out in the changelog. Pin your version and read the release notes before upgrading, and you won't be surprised.
Honest: it's beta.
Express and Fastify have a decade of ecosystem behind them; NestJS has mature enterprise tooling. Green Tea doesn't pretend otherwise. Choose it today because the model fits your application — explicit dependencies, derived execution, inspectable infrastructure, one real-time mental model, and a foundation you don't have to assemble. If your team needs a large plugin catalogue today, that gap is real, and the API can still change before stable.
That's the tea. 🍵