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.

sessionauthuserauthorizationpermissionshandler

  • 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 friends provider, then ask for friends — 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 /prices

    markethandler

    A public endpoint never asks for a user, so authentication is not in its graph slice.

  • GET /portfolio

    sessionauthuserportfoliohandler

    A 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.

AsyncIterableSSE · 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.

CapabilityExpressFastifyNestJSGreen Tea.
The pipeline isa positional chainhooks + pluginsDI + guards/interceptorsa graph, topologically sorted
Order comes fromthe line you wrote it onhook phase + registrationmodule wiringwhat each step needs
Is req.user there?hope sohope soif the guard ranboot fails if nothing provides it
See the pipelineread the coderead the codepaid Devtoolsexplain / graph, free
Real-timebolt on a ws/sse liba plugina separate gatewayone AsyncIterable primitive
Runs onNodeNodeNodeNode · Deno · Bun · edge
Runtime depsminimalminimalheavy (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. 🍵