Skip to content

AsenaJs/Asena

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Asena

Asena is a NestJS-like IoC web framework built on top of Hono and Bun. It combines the power of dependency injection with the performance of Bun runtime and the flexibility of Hono web framework.

Documentation

For detailed documentation, please visit not ready yet. Documentation is still in progress, but updates are being made regularly.

Key Features

  • Dependency Injection: Built-in IoC container for managing dependencies
  • Decorator-based Development: Similar to NestJS, using TypeScript decorators for routing and DI
  • High Performance: Leverages Bun runtime for optimal performance
  • WebSocket Support: Built-in WebSocket handling capabilities
  • Middleware System: Flexible middleware architecture
  • HTTP Adapters: Extensible adapter system with Hono as default

Quick Start

First, create a new project using Bun:

bun init

For decorators working properly, you need to add some settings to your tsconfig. Here is an recommended file:

{
  "compilerOptions": {
    // Enable latest features
    "lib": ["ESNext", "DOM"],
    "target": "ESNext",
    "module": "ESNext",
    "moduleDetection": "force",
    "jsx": "react-jsx",
    "allowJs": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,

    // Bundler mode
    "moduleResolution": "bundler",
    "allowImportingTsExtensions": true,
    "verbatimModuleSyntax": true,
    "noEmit": true,

    // Best practices
    "strict": false,
    "skipLibCheck": true,
    "noFallthroughCasesInSwitch": true,

    // Some stricter flags
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noPropertyAccessFromIndexSignature": true
  }
}

Then, install the required packages:

bun add @asenajs/asena hono winston

Add @asenajs/asena-cli to your package. This package provides a CLI for creating and managing Asena projects.

bun add -D @asenajs/asena-cli

Then, create a new asena-config.ts file using the CLI:

## Creates a asena-config.ts file with default values (requires manual updates). Source folder is 'src'.
asena init

Note: Built options directly copy of bun options, you can check bun documentation for more options. Bun Documentation

Create index.ts file under your src folder:

// src/index.ts
const [adapter, serverLogger] = createHonoAdapter(new DefaultLogger());
await new AsenaServer(adapter, serverLogger).port(3000).start(true);

To run asena you need at least one controller. Create a new controller:

// src/controllers/TestController.ts
import type { Context } from '@asenajs/asena/adapter/hono';
import { Controller } from '@asenajs/asena/server';
import { Get } from '@asenajs/asena/web';

@Controller('/hello')
export class TestController {

    @Get('/world')
    public async getHello(context: Context) {
        return context.send('Hello World');
    }

}

Finally, run the project:

## only for fast developing purposes
asena dev start
## or you can simply build then run your bundled project
asena build
## then go to dist folder and run the project this way it will consume less memory
bun index.asena.js

Example

Here is a simple example of a controller with service and middleware:

Middleware

This middleware sets a value to the context object.

// src/middleware/TestMiddleware.ts
import type { AsenaNextHandler } from '@asenajs/asena/adapter';
import type { Context, MiddlewareService } from '@asenajs/asena/adapter/hono';
import { Middleware } from '@asenajs/asena/server';

@Middleware()
export class TestMiddleware implements MiddlewareService {
  public handle(context: Context, next: AsenaNextHandler) {
    context.setValue('testValue', 'test');

    next();
  }
}

Service

Basic service with a getter and setter.

import { Service } from '@asenajs/asena/server';

@Service()
export class HelloService {
  private _foo = 'bar';

  public get foo(): string {
    return this._foo;
  }

  public set foo(value: string) {
    this._foo = value;
  }
}

Controller

Controller with a GET route that uses the middleware and service.

// src/controller/TestController.ts
import { type Context, Controller, Get, Inject } from '@asenajs/asena';
import { HelloService } from '../service/HelloService.ts';
import { TestMiddleware } from '../middleware/TestMiddleware.ts';

@Controller('/v1')
export class TestController {
  @Inject(HelloService)
  private helloService: HelloService;

  @Get('foo')
  public async getFoo(context: Context) {
    return context.send(this.helloService.foo);
  }

  @Get({ path: 'world', middlewares: [TestMiddleware] })
  public async getHello(context: Context) {
    const testValue: string = context.getValue('testValue');

    return context.send(testValue);
  }
}

Index

The main file that starts the server.

// src/index.ts
import { AsenaServer, createHonoAdapter } from '@asenajs/asena';
import { DefaultLogger } from '@asenajs/asena/logger';

const [adapter, serverLogger] = createHonoAdapter(new DefaultLogger());
await new AsenaServer(adapter, serverLogger).port(3000).start(true);

then run

asena dev start

You should see the following output:


Build completed successfully.
2024-11-19 17:58:35 [info]:
    ___    _____  ______ _   __ ___
   /   |  / ___/ / ____// | / //   |
  / /| |  \__ \ / __/  /  |/ // /| |
 / ___ | ___/ // /___ / /|  // ___ |
/_/  |_|/____//_____//_/ |_//_/  |_|

2024-11-19 17:58:35 [info]:     IoC initialized
2024-11-19 17:58:35 [info]:     No server services found
2024-11-19 17:58:35 [info]:     Controller: V1 found
2024-11-19 17:58:35 [info]:     Successfully registered GET route for PATH: /v1/foo
2024-11-19 17:58:35 [info]:     Successfully registered GET route for PATH: /v1/world
2024-11-19 17:58:35 [info]:     No websockets found
2024-11-19 17:58:35 [info]:     Server started on port 3000

and you see the result on your browser http://localhost:3000/v1/foo with "bar" message.

Note: For more example, you can check this project Example.

Core Concepts

  • Controllers: Handle incoming requests using decorators
  • Services: Business logic containers that can be injected
  • ServerSrvices: Server-wide services that can be injected (e.g. Database)
  • Middleware: Request/Response interceptors
  • Adapters: Framework adapters (default: Hono)
  • WebSocket: Real-time communication support
  • Validators: Request validation system

Architecture

Asena follows a modular architecture with:

  • IoC Container for dependency management
  • Adapter pattern for framework integration
  • Decorator-based routing system
  • Middleware pipeline
  • WebSocket integration
  • Context management

Performance

Built on Bun runtime and Hono, Asena provides:

  • Fast startup time
  • Low memory footprint
  • Quick request processing
  • Efficient WebSocket handling

and here is the benchmark result of Asena with Hono adapter(Basic hello world example) in every test we used bun to run the project:

Performance Comparison (Fastest to Slowest)

Framework Requests/sec Latency (avg) Transfer/sec
Hono 147,031.08 2.69ms 18.09MB
Asena 137,148.80 2.89ms 16.74MB
NestJS (Bun + Fastify) 81,652.05 6.01ms 13.78MB
NestJS (Bun) 64,435.83 6.14ms 11.80MB
NestJS (Bun + kiyasov/platform-hono) 45,082.27 8.79ms 5.55MB
NestJS (Node) 17,649.89 24.43ms 4.02MB

Benchmark conditions:

  • 12 threads
  • 400 connections
  • 120 seconds duration
  • Simple "Hello World" endpoint
  • Running on same hardware

Note: Lower latency and higher requests/sec indicate better performance