DEV Community

Cover image for Introducing YasuiJS — A Modern, Minimal REST Framework for Any Runtime
Thomas BARKATS
Thomas BARKATS

Posted on

Introducing YasuiJS — A Modern, Minimal REST Framework for Any Runtime

Most backend frameworks today are built on foundations from a different era. Express sits on Node.js HTTP. NestJS sits on Express. Each layer adds abstraction, dependencies, and constraints. What if we started fresh?

YasuiJS is a new TypeScript framework for building REST APIs, built on Web Standards from day one. No Express underneath. No Node.js HTTP layer. Just the Fetch API, Web Standards Request/Response, and a lightweight server adapter (srvx) that works across runtimes.

The result? A framework that runs anywhere—Node.js, Deno, Bun, Cloudflare Workers, Vercel Edge—with the elegant decorator-driven DX you'd expect from modern frameworks, but without the bloat.

Here's what it looks like:

@Controller('/api/users')
export class UserController {
  constructor(private userService: UserService) {}

  @Get('/:id')
  @ApiOperation('Get user by ID')
  async getUser(
    @Param('id') id: number,
    @Query('active') active: boolean
  ) {
    return this.userService.getUserById(id, active);
  }
}
Enter fullscreen mode Exit fullscreen mode

If you've used NestJS, this syntax feels familiar. That's intentional—YasuiJS takes inspiration from patterns that work. Class-based, decorators.

Built on Web Standards, Not Legacy Layers

YasuiJS is not built on top of Express or Node.js HTTP. It's built directly on the Fetch API and Web Standards Request/Response.

The Architecture Difference

Traditional Stack:

Your Code → NestJS → Express → Node.js HTTP
Enter fullscreen mode Exit fullscreen mode

YasuiJS Stack:

Your Code → YasuiJS → srvx (Web Standards adapter) → Runtime (Node/Deno/Bun)
Enter fullscreen mode Exit fullscreen mode

Web Standards are platform-agnostic. The same Request/Response objects work on Node.js, Deno, Bun, Cloudflare Workers, Vercel Edge, AWS Lambda@Edge, anywhere that implements the Fetch API.

Multi-Runtime Support

Because YasuiJS is built on Web Standards from day one, multi-runtime support isn't bolted on—it's inherent.

Write your API once:

@Controller('/api/products')
export class ProductController {
  constructor(private productService: ProductService) {}

  @Get('/')
  getProducts() {
    return this.productService.findAll();
  }
}
Enter fullscreen mode Exit fullscreen mode

Deploy it anywhere:

// Node.js / Bun
yasui.createServer({ controllers: [ProductController], port: 3000 });

// Deploy to Cloudflare Workers
export default { fetch: app.fetch };

// Deploy to Vercel Edge
export const GET = app.fetch;
export const POST = app.fetch;

// Deploy to Deno Deploy
Deno.serve(app.fetch);
Enter fullscreen mode Exit fullscreen mode

Same code. Same imports. Same dependencies. Different runtime, zero changes.

This matters because:

  • Edge computing is moving REST APIs closer to users
  • Deno and Bun are production-ready alternatives to Node.js
  • Serverless platforms increasingly support Web Standards
  • Future runtimes will support Fetch API, not Express

NestJS can't do this. It's fundamentally tied to Node.js and Express's architecture. YasuiJS was designed for a multi-runtime world from the start.

Flexibility Without Boilerplate

No Module System

YasuiJS doesn't have modules. Not because modules are bad, but because for REST APIs, they're unnecessary overhead.

In NestJS, adding a new endpoint means:

  1. Create the service
  2. Add it to a module's providers
  3. Import that module wherever you need it
  4. Export if other modules need it
  5. Update the dependency graph

In YasuiJS:

  1. Create the service with @Injectable()
  2. Inject it in your controller

That's it. The DI system resolves the dependency graph automatically. No modules, no providers arrays, no imports/exports to manage.

@Injectable()
class UserService {
  // Business logic
}

@Controller('/users')
class UserController {
  constructor(private userService: UserService) {} // Just works
}

yasui.createServer({ controllers: [UserController] });
Enter fullscreen mode Exit fullscreen mode

Automatic Type Casting Everywhere

YasuiJS reads your TypeScript types and automatically converts parameters at runtime. Not just in controllers—everywhere you use parameter decorators.

// In controllers
@Get('/:id')
getUser(
  @Param('id') id: number,           // Converted to number
  @Query('active') active: boolean   // Converted to boolean
) { }

// In middlewares (same pattern!)
@Middleware()
class RateLimiter {
  use(
    @Query('apiKey') key: string,
    @Header('x-rate-limit') limit: number  // Also converted!
  ) { }
}
Enter fullscreen mode Exit fullscreen mode

No pipes to configure for basic type casting. But when you need custom validation or transformation, YasuiJS provides Pipes that can be applied at global, controller, or method level—works seamlessly with class-validator.

Consistent Patterns Everywhere

In YasuiJS, middlewares work exactly like controllers. Same decorators. Same DI. Same parameter extraction. Same type casting.

@Middleware()
export class AuthMiddleware {
  constructor(private authService: AuthService) {}  // DI works

  use(
    @Header('authorization') token: string,         // Decorators work
    @Query('version') version: number,              // Type casting works
    @Inject() logger: LoggerService                 // Method injection works
  ) {
    if (!this.authService.verify(token)) {
      throw new HttpError(401, 'Unauthorized');
    }
    logger.log(`Auth successful, API v${version}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

You learn one pattern—controllers—and it works everywhere. One mental model for the entire framework.

Performance: A Result of Clean Architecture

When you eliminate abstraction layers and build on modern foundations, performance follows naturally.

Benchmark setup: Identical REST API (3 controllers, 9 endpoints, full DI), Node.js v22, Windows 11, 10-second load test per endpoint:

Framework Requests/sec Avg Latency Cold Start
YasuiJS 6,850 0.98ms 247ms
NestJS 5,442 1.31ms 582ms
Express 5,505 1.31ms 15ms

YasuiJS is 25% faster than NestJS in request throughput and more than 50% faster to cold start (critical for serverless).

Why?

  • No Express layer: Direct to Web Standards reduces overhead
  • Radix3 router: Efficient route matching algorithm
  • Cached DI resolution: Dependencies resolved once at startup
  • Pre-computed metadata: Decorators processed during initialization, not per-request

Clean architecture produces fast code. It's not about micro-optimizations—it's about fewer layers doing less work.

What YasuiJS Is (And Isn't)

YasuiJS Is For:

  • REST APIs: HTTP endpoints, CRUD operations, RESTful services
  • Multi-runtime deployment: Need to run on Node, Deno, Bun, or edge without code changes
  • Modern architectures: Serverless, edge computing, distributed systems
  • Clean patterns: Class-based, decorator-driven, dependency injection
  • TypeScript-first: Full type safety with automatic runtime type conversion
  • Performance-critical apps: Where cold start and throughput matter

YasuiJS Is Not:

  • Not a full framework: No built-in GraphQL, WebSockets, or microservices (REST focus)
  • Not batteries-included: You bring your own database, validation, authentication libraries
  • Not trying to replace NestJS: Different goals, different trade-offs

Choose YasuiJS When:

  • You're building REST APIs specifically
  • You need multi-runtime flexibility (Node/Deno/Bun/edge)
  • You want NestJS-style patterns without the module system
  • Performance and bundle size are constraints (serverless/edge scenarios)
  • You prefer focused tools over comprehensive frameworks

Choose NestJS When:

  • You need more than REST (GraphQL, WebSockets, CQRS, microservices)
  • You want comprehensive integrations (Passport, TypeORM, Prisma, Bull, etc.)
  • You're building large enterprise applications with complex requirements
  • You need mature ecosystem and large community support

Getting Started

Install YasuiJS (and why not with Bun !):

bun install yasui
Enter fullscreen mode Exit fullscreen mode

Configure TypeScript (enable experimentalDecorators and emitDecoratorMetadata):

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    // ... other options
  }
}
Enter fullscreen mode Exit fullscreen mode

Create your first API:

import yasui, { Controller, Get, Post, Body, Injectable } from 'yasui';

@Injectable()
class UserService {
  private users = [{ id: 1, name: 'Alice' }];

  getAll() { return this.users; }

  create(name: string) {
    const user = { id: this.users.length + 1, name };
    this.users.push(user);
    return user;
  }
}

@Controller('/api/users')
class UserController {
  constructor(private userService: UserService) {}

  @Get('/')
  getUsers() {
    return this.userService.getAll();
  }

  @Post('/')
  createUser(@Body('name') name: string) {
    return this.userService.create(name);
  }
}

yasui.createServer({
  controllers: [UserController],
  swagger: { generate: true, path: '/docs' }
});
Enter fullscreen mode Exit fullscreen mode

Run it:

bun run app.ts
Enter fullscreen mode Exit fullscreen mode

Visit http://localhost:3000/api/users for your API.
Visit http://localhost:3000/docs for auto-generated Swagger UI.

That's it. No modules to configure. No providers to declare. Just controllers and services.

Key Features at a Glance

Beyond what we've covered, YasuiJS includes:

  • Automatic Swagger/OpenAPI generation - Document as you code with flexible decorators
  • Built-in services - LoggerService, ConfigService ready to use
  • Flexible DI scopes - SHARED (singleton), LOCAL (per-request), DEEP_LOCAL (isolated)
  • TLS/HTTPS & HTTP/2 support - Production-ready security out of the box
  • Express-compatible Request - Web Standards with familiar Express properties for easy migration
  • Comprehensive error handling - Throw anywhere, framework catches and formats
  • Method-level injection - Inject dependencies at constructor or method level
  • Validation pipes - Custom validation/transformation at any level, integrates with class-validator

The Future of REST APIs

The web platform is evolving. Edge computing brings computation closer to users. Deno and Bun offer modern alternatives to Node.js. Web Standards provide a common foundation across all of them.

YasuiJS was built for this future. Not as a reaction to existing frameworks, but as a ground-up rethinking of what a REST framework should be in a multi-runtime world.

If you're building REST APIs and want:

  • Modern architecture based on Web Standards
  • Multi-runtime flexibility without code changes
  • Clean patterns without unnecessary boilerplate
  • Performance that scales from edge to cloud

YasuiJS might be what you're looking for.

Learn More

YasuiJS is open source (AGPL-3.0) and actively developed. Try it for your next REST API project and see if the approach resonates with you.


What are your thoughts on Web Standards vs framework-specific abstractions? Are you deploying to edge runtimes? Let me know in the comments.

Top comments (0)