Skip to content

A Simple, Clean, Flexible and Modulable web framework project, based on Express and Typescript

License

Notifications You must be signed in to change notification settings

Expressive-Tea/expresive-tea

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

278 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

npm version downloads vulnerabilities coverage build stars license


Logo

Expressive Tea

A modern, TypeScript-first framework for building scalable Node.js applications
Clean architecture β€’ Dependency Injection β€’ Decorator-driven β€’ Express-powered

πŸ“š Documentation Β· πŸš€ Live Demo Β· πŸ› Report Bug Β· πŸ’‘ Request Feature


Important

πŸ“¦ Package Renamed: @expressive-tea/core

Expressive Tea has a new home on npm! Starting with v2.0.0, install using:

npm install @expressive-tea/core

Legacy package @zerooneit/expressive-tea will be maintained until April 30, 2026 for security patches only. Please migrate to @expressive-tea/core as soon as possible.

Why the change?

  • ✨ Better namespace organization (@expressive-tea/*)
  • 🌍 Community-focused ownership
  • πŸš€ Clearer project identity

Migration is simple: Just update your package.json and imports remain the same!

- "dependencies": { "@zerooneit/expressive-tea": "^1.2.0" }
+ "dependencies": { "@expressive-tea/core": "^2.0.0" }

Caution

⚠️ CRITICAL: v1.x Security Notice

All versions 1.x are DEPRECATED and UNSUPPORTED as of January 27, 2026.

v1.3.x Beta - πŸ”΄ CRITICAL SECURITY VULNERABILITY - DO NOT USE
Contains critical cryptography flaws in Teapot/Teacup gateway. If you're using this, STOP IMMEDIATELY and upgrade to v2.0.0.

v1.2.x Production - 🟑 No crypto issues, but deprecated (InversifyJS v6 EOL)

πŸ‘‰ Upgrade to v2.0.0 NOW - See Migration Guide


⚑ Quick Start

# Install the new package
npm install @expressive-tea/core

# Or with yarn
yarn add @expressive-tea/core
import { ServerSettings, Route, Get, Boot } from '@expressive-tea/core';

@ServerSettings({ port: 3000 })
class App extends Boot {}

@Route('/hello')
class HelloController {
  @Get('/')
  sayHello() {
    return { message: 'Hello, World! 🍡' };
  }
}

new App().start();
// πŸŽ‰ Server running on http://localhost:3000

Try it live on CodeSandbox β†’


🎯 Why Expressive Tea?

The Problem

Building Node.js applications is powerful, but messy. You get a blank canvas with Expressβ€”no structure, no conventions, just middleware chaos. Sound familiar?

The Solution

Expressive Tea brings the elegance of modern frameworks to Node.js, without the bloat. Think NestJS simplicity meets Express flexibility.

🌟 What Makes It Special

Feature What You Get
🎨 Clean Architecture Decorators organize your code beautifullyβ€”no more spaghetti routes
πŸ”Œ Plugin Everything Share database configs, auth, websockets across projects
πŸ’‰ Smart DI Singleton, Transient, Scoped servicesβ€”InversifyJS under the hood
πŸ›‘οΈ Type-Safe Full TypeScript strict modeβ€”catch bugs before they ship
πŸ”’ Secure by Default AES-256-GCM + HKDF crypto, built-in security best practices
⚑ Production Ready 92%+ test coverage, battle-tested in real applications
🎯 Express Compatible Use ANY Express middlewareβ€”gradual migration friendly
πŸ“¦ Zero Lock-in BYOA (Bring Your Own Architecture)β€”we don't force opinions

πŸš€ What's New in v2.0

Major security and architecture improvements!

+ βœ… Security: Fixed critical crypto vulnerabilities (AES-256-GCM + HKDF)
+ βœ… Type Safety: Full TypeScript strict mode support
+ βœ… DI: Scoped dependency injection (Singleton/Transient/Scoped)
+ βœ… Health Checks: Built-in health endpoints for Kubernetes/monitoring
+ βœ… Environment: .env file support with @Env decorator
+ βœ… Performance: Native utilities, removed lodash dependencies
+ βœ… ESLint: Migrated to ESLint v9 flat config
+ βœ… Quality: 95%+ coverage, all tests passing

⚠️ Breaking Changes:

  • Cryptography format changed (must re-encrypt data)
  • TypeScript strict mode enabled
  • Node.js 20+ required (Node.js 18 reached EOL April 2025)
  • Express 5.x required
  • ESLint v9 (flat config)

πŸ“– Full Changelog β€’ πŸ”„ Migration Guide


πŸ’‘ Features That'll Make You Smile

🎨 Decorator-Driven Development

@Route('/api/users')
class UserController {
  @Get('/:id')
  async getUser(@Param('id') id: string) {
    return this.userService.findById(id);
  }

  @Post('/')
  async createUser(@Body() data: CreateUserDto) {
    return this.userService.create(data);
  }
}

πŸ”Œ Pluggable Architecture

import { AuthPlugin } from '@my-org/auth-plugin';
import { DatabasePlugin } from '@my-org/db-plugin';

@ServerSettings({
  port: 3000,
  plugins: [AuthPlugin, DatabasePlugin]
})
class App extends Boot {}

πŸ’‰ Dependency Injection

@injectable()
class UserService {
  constructor(
    @inject(TYPES.Database) private db: Database,
    @inject(TYPES.Logger) private logger: Logger
  ) {}
}

🎯 Type-Safe Everything

// Generics everywhere
class ApiResponse<T> {
  constructor(
    public data: T,
    public status: number
  ) {}
}

@Get('/users')
getUsers(): ApiResponse<User[]> {
  return new ApiResponse(users, 200);
}

πŸ₯ Built-in Health Checks

@HealthCheck({
  checks: [
    {
      name: 'database',
      check: async () => {
        const isConnected = await db.ping();
        return { status: isConnected ? 'pass' : 'fail' };
      },
      critical: true, // Blocks readiness probe if fails
      timeout: 5000
    }
  ]
})
class App extends Boot {}

// Endpoints:
// GET /health       - Detailed health status
// GET /health/live  - Liveness probe (K8s)
// GET /health/ready - Readiness probe (K8s)

🌍 Environment Variable Support

// Load from .env files
@Env({ path: '.env', required: ['DATABASE_URL', 'API_KEY'] })
@Env({ path: '.env.local', override: true, silent: true })
class App extends Boot {}

// In your .env:
// DATABASE_URL=postgres://localhost:5432/mydb
// API_KEY="secret-key"

🎯 Type-Safe Environment Variables (v2.0.1+)

import { z } from 'zod';

const EnvSchema = z.object({
  PORT: z.string().transform(Number),
  DATABASE_URL: z.string().url(),
  API_KEY: z.string().min(32)
});

type Env = z.infer<typeof EnvSchema>;

@Env<Env>({
  transform: (env) => EnvSchema.parse(env),
  onTransformError: 'throw' // Fail fast on invalid env
})
class App extends Boot {
  constructor() {
    super();
    const env = Settings.getInstance().getEnv<Env>();
    console.log(env.PORT); // Type: number (validated!)
  }
}

πŸ“„ Configuration Files (v2.0.1+)

# .expressive-tea.yaml (YAML support!)
port: 3000
securePort: 4443

database:
  host: localhost
  port: 5432

# Comments supported!
cache:
  enabled: true
  ttl: 3600

File Priority: .expressive-tea.yaml > .expressive-tea.yml > .expressive-tea (JSON)


πŸ“¦ Installation & Setup

Prerequisites

  • Node.js β‰₯ 20.0.0
  • TypeScript β‰₯ 5.0.0
  • Express β‰₯ 5.0.0

Note: Node.js 18 support was dropped in v2.0.0 as it reached End-of-Life in April 2025. We recommend using Node.js 20 LTS or Node.js 22 for the best experience and security updates.

Configure TypeScript

{
  "compilerOptions": {
    "target": "ES2017",
    "module": "commonjs",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    
    // Recommended for maximum safety
    "strict": true,
    "strictNullChecks": true,
    "noImplicitAny": true
  }
}

Install

# npm
npm install @expressive-tea/core reflect-metadata

# yarn
yarn add @expressive-tea/core reflect-metadata

Local staging with Verdaccio

If you want to test publishing locally before pushing to the public registry, use a local Verdaccio instance as a staging registry.

Quick steps:

  1. Start Verdaccio (Docker):
docker run -d --rm --name verdaccio-expressive-tea -p 4873:4873 verdaccio/verdaccio:latest
  1. Point npm to local registry and publish:
# point npm to local registry
npm set registry http://localhost:4873

# publish (from package root)
npm publish --registry http://localhost:4873

# restore default registry
npm set registry https://registry.npmjs.org/
  1. Optional: Use the repo-provided Verdaccio config for deterministic behavior:
docker run -d --rm --name verdaccio-expressive-tea -p 4873:4873 \
  -v $(pwd)/.docs/verdaccio/config.yaml:/verdaccio/conf/config.yaml \
  verdaccio/verdaccio:latest

Notes:

  • Default URL: http://localhost:4873
  • Container name: verdaccio-expressive-tea (the agent checks for this name before starting a new container)
  • Anonymous publishing is enabled in the example config (local only). Do not expose to public networks.
  • The config permits overwriting the same package version for easy iterative testing.

Your First App

1. Create your server:

// server.ts
import 'reflect-metadata';
import { ServerSettings, Boot } from '@expressive-tea/core';

@ServerSettings({
  port: 3000,
  controllers: [HelloController]
})
class MyApp extends Boot {}

export default MyApp;

2. Add a controller:

// controllers/hello.controller.ts
import { Route, Get } from '@expressive-tea/core';

@Route('/hello')
export class HelloController {
  @Get('/')
  sayHello() {
    return { message: 'Hello, Expressive Tea! 🍡' };
  }
}

3. Start it up:

// main.ts
import MyApp from './server';

const app = new MyApp();
app.start().then(() => {
  console.log('πŸš€ Server is running!');
});

πŸ“š Full Tutorial β†’


πŸŽ“ Learn More

πŸ“– Documentation

πŸ†• v2.0.1 Features

πŸ”„ Migration & Upgrading

πŸ›‘οΈ Security


🀝 Contributing

We love contributions! Whether it's bug fixes, features, or docs.

Quick links:

# Get started
git clone https://github.com/Expressive-Tea/expresive-tea.git
cd expresive-tea
yarn install
yarn test

πŸ€– AI-Assisted Development & Vibe Coding

We welcome AI-assisted contributions! Whether you're using GitHub Copilot, Cursor, Claude, or other AI coding assistants, we embrace the future of collaborative development.

⚠️ IMPORTANT: AI-Generated Code Requirements

If you're using AI tools for code generation, you MUST:

  1. πŸ“– Follow Repository Guidelines

    • βœ… Read and strictly adhere to AGENTS.md - Agent-specific coding rules
    • βœ… Read and strictly adhere to CLAUDE.md - Claude AI guidelines
    • βœ… These files contain critical project conventions, style guides, and quality standards
  2. πŸ‘¨β€πŸ’» Human Review is MANDATORY

    • βœ… All AI-generated code MUST be reviewed by a human developer before creating a pull request
    • βœ… Understand the code completelyβ€”don't submit code you can't explain
    • βœ… Test thoroughly (aim for 95%+ coverage)
    • βœ… Verify the code follows our architectural patterns and best practices
  3. βœ… Quality Standards

    • βœ… All tests must pass (yarn test)
    • βœ… Linting must pass (yarn linter:ci)
    • βœ… TypeScript must compile without errors (yarn build)
    • βœ… Code must match our existing patterns and conventions
    • βœ… Documentation must be updated (JSDoc, README, CHANGELOG)
  4. πŸ“ PR Transparency

    • βœ… Disclose AI assistance in your pull request description
    • βœ… Example: "This PR was developed with assistance from Claude/Copilot/Cursor"
    • βœ… Highlight any sections that were fully AI-generated for extra review

Why These Rules?

  • πŸ›‘οΈ Quality Assurance - AI can make subtle mistakes humans catch
  • 🎯 Consistency - Ensures code matches our architectural vision
  • πŸ“š Knowledge Transfer - Reviewers understand your contribution
  • πŸ”’ Security - Prevents AI from introducing vulnerabilities
  • 🀝 Collaboration - Maintains clear communication in the codebase

Vibe Coding Best Practices:

// βœ… GOOD: AI-generated, reviewed, and refined by human
@Route('/api/users')
class UserController {
  @Get('/:id')
  async getUser(@Param('id') id: string): Promise<User> {
    // Human: Added validation per AGENTS.md security guidelines
    if (!id || !validator.isUUID(id)) {
      throw new BadRequestException('Invalid user ID');
    }
    return this.userService.findById(id);
  }
}

// ❌ BAD: AI-generated, unreviewed, missing error handling
@Route('/api/users')
class UserController {
  @Get('/:id')
  async getUser(@Param('id') id: string) {
    return this.userService.findById(id); // What if id is invalid?
  }
}

πŸ“š Required Reading for AI-Assisted Development:

Questions? Ask in GitHub Discussions before submitting AI-generated code.


πŸ’¬ Community & Support

Get Help

Stay Connected


🌟 Built With

Technology Purpose
Express Fast, unopinionated web framework
TypeScript Type-safe JavaScript
InversifyJS Powerful dependency injection
Reflect Metadata Decorator metadata support

πŸ† Sponsors

Building Expressive Tea takes time and dedication. If this project helps you, consider sponsoring!

Principal Sponsor:

Zero-OneIT

Interested in sponsoring? Contact projects@zero-oneit.com


πŸ“„ License

Apache-2.0 License - see LICENSE file for details


πŸ“Œ Versioning

We use Semantic Versioning (SemVer). See tags for available versions.


πŸ‘₯ Contributors

Lead Developer: Diego Resendez

See all contributors who've helped shape Expressive Tea.


❀️ Credits

Logo and banner designed by Freepik


Made with β˜• and 🍡 by the Expressive Tea Team
Start brewing better Node.js apps today!

About

A Simple, Clean, Flexible and Modulable web framework project, based on Express and Typescript

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors