Skip to content

Latest commit

Β 

History

History
415 lines (342 loc) Β· 10.5 KB

File metadata and controls

415 lines (342 loc) Β· 10.5 KB

AsenaJS Example Project πŸš€

A comprehensive example application showcasing AsenaJS framework features through Todo and Chat applications with real-time WebSocket communication.

🎯 About AsenaJS

AsenaJS is a modern TypeScript framework for building scalable server-side applications with:

  • Decorator-based architecture - Clean, declarative code
  • Dependency Injection - Built-in IoC container
  • WebSocket support - First-class real-time communication
  • Database integration - Seamless ORM integration (Drizzle)
  • Type safety - Full TypeScript support throughout

✨ Features Demonstrated

Core AsenaJS Features

1. Dependency Injection

@Service()
export class TodoService {
  @Inject("TodoRepository")
  // @Inject(TodoRepository) you can use like that also
  private todoRepository: TodoRepository;
}

2. Decorator-based Routing

@Controller({ path: '/todos', middlewares: [AuthMiddleware] })
export class TodoController {
  @Get({ path: '/', description: 'Get all todos' })
  public async getTodos(context: Context) { }
}

3. Repository Pattern with Drizzle ORM

import {BunSQLDatabase} from "drizzle-orm/bun-sql";

@Repository({
  databaseService: 'DatabaseService',
  table: TodoSchema,
})
export class TodoRepository extends BaseRepository<TodoSchemaType, BunSQLDatabase> {
  public async getTodosByUserId(userId: string): Promise<Todo[]> {
    return await this.db!.select().from(TodoSchema);
  }
}

4. WebSocket Support

@WebSocket({ path: 'chat-room', middlewares: [WSAuthMiddleware] })
export class ChatWebSocket extends AsenaWebSocketService<{ user: User }> {
  @Inject("ChatService")
  private chatService: ChatService;
  
  public async onMessage(ws: Socket, message: Buffer | string) {
    // Real-time message handling
  }
}

5. Middleware System

@Middleware()
export class AuthMiddleware implements MiddlewareService {
  public async handle(context: Context, next: Next) {
    // Authentication logic
  }
}

6. Validation with Zod

@Middleware({ validator: true })
export class CreateTodoValidator extends ValidationService {
  public json() {
    return z.object({
      title: z.string(),
      description: z.string(),
      isCompleted: z.boolean(),
    });
  }
}

πŸ—οΈ Project Architecture

Clean Architecture Layers

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚         Controllers                 β”‚  ← REST & WebSocket endpoints
β”‚  @Controller, @WebSocket            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚          Services                   β”‚  ← Business logic
β”‚          @Service                   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚        Repositories                 β”‚  ← Data access
β”‚  @Repository (BaseRepository)       β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
               β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚      Database (Drizzle ORM)         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Project Structure

src/
β”œβ”€β”€ controller/          # REST & WebSocket controllers
β”‚   β”œβ”€β”€ TodoController.ts
β”‚   β”œβ”€β”€ ChatController.ts
β”‚   └── AuthController.ts
β”œβ”€β”€ core/
β”‚   β”œβ”€β”€ repository/      # @Repository classes
β”‚   β”‚   β”œβ”€β”€ TodoRepository.ts
β”‚   β”‚   └── ChatRepository.ts
β”‚   β”œβ”€β”€ service/         # @Service classes
β”‚   β”‚   β”œβ”€β”€ TodoService.ts
β”‚   β”‚   └── ChatService.ts
β”‚   └── schemas/         # Drizzle ORM schemas
β”‚       β”œβ”€β”€ User.ts
β”‚       β”œβ”€β”€ Todo.ts
β”‚       └── Chat.ts
β”œβ”€β”€ websocket/           # WebSocket handlers
β”‚   β”œβ”€β”€ ChatWebSocket.ts          # Authenticated
β”‚   └── NotificationWebSocket.ts  # Public
└── middleWare/
    β”œβ”€β”€ auth/            # Authentication
    └── validator/       # Request validation

πŸš€ Quick Start

1. Install Dependencies

bun install

2. Setup Database

# Create PostgreSQL database
psql -U postgres -c "CREATE DATABASE asenatest;"

# Generate migrations from schemas
bun run drizzle:generate

# Apply migrations
bun run drizzle:migrate

3. Start Server

bun run build:start

Server runs on http://localhost:3000

4. Test WebSockets

Open http://localhost:3000/websocket-test.html for interactive WebSocket testing.

πŸ“‹ API Examples

Todo API (with Authentication)

Create Todo:

POST /todos
Authorization: Cookie
{
  "title": "Learn AsenaJS",
  "description": "Study the framework",
  "isCompleted": false
}

Get All Todos:

GET /todos
Authorization: Cookie

Chat API (with Authentication)

Create Chat Room:

POST /chat/rooms
{
  "name": "General",
  "description": "Main chat room"
}

Send Message:

POST /chat/messages
{
  "content": "Hello, World!",
  "roomId": "room-uuid"
}

πŸ”Œ WebSocket Examples

1. Chat WebSocket (Authenticated)

// Requires authentication cookie
const chatWs = new WebSocket('ws://localhost:3000/chat-room');

chatWs.onopen = () => {
  // Join a room and send message
  chatWs.send(JSON.stringify({
    type: 'message',
    content: 'Hello everyone!',
    roomId: 'room-uuid'
  }));
};

chatWs.onmessage = (event) => {
  const data = JSON.parse(event.data);
  console.log('Received:', data);
};

2. Notification WebSocket (Public)

// No authentication required
const notifWs = new WebSocket('ws://localhost:3000/notifications');

notifWs.onopen = () => {
  // Subscribe to a channel
  notifWs.send(JSON.stringify({
    type: 'subscribe',
    channel: 'updates'
  }));
};

notifWs.onmessage = (event) => {
  const notification = JSON.parse(event.data);
  console.log('Notification:', notification);
};

πŸŽ“ Key AsenaJS Concepts

1. Dependency Injection

AsenaJS uses a powerful IoC container. Dependencies are automatically resolved:

@Service()
export class ChatService {
  @Inject(ChatRepository)  // Automatically injected
  private chatRepository: ChatRepository;
}

2. Decorator-based Configuration

Everything is configured through decorators:

  • @Controller() - Define REST controllers
  • @Service() - Define services
  • @Repository() - Define data repositories
  • @WebSocket() - Define WebSocket handlers
  • @Middleware() - Define middlewares
  • @Get(), @Post(), @Put(), @Delete() - Define routes

3. Middleware Chain

Middlewares can be applied at controller or route level:

@Controller({ 
  path: '/todos', 
  middlewares: [AuthMiddleware] // Applied to all routes
})
export class TodoController {
  @Post({ 
    path: '/', 
    validator: CreateTodoValidator // Route-specific
  })
  public async create(context: Context) { }
}

4. WebSocket with Authentication

WebSockets can use middlewares just like REST endpoints:

@WebSocket({ 
  path: 'chat-room', 
  middlewares: [WSAuthMiddleware] // Auth required
})
export class ChatWebSocket extends AsenaWebSocketService<{ user: User }> {
  public onMessage(ws: Socket<{ user: User }>, message: Buffer | string) {
    // User data available from ws.data.user
  }
}

5. Type-Safe Database Access

Using Drizzle ORM with AsenaJS:

@Repository({
  databaseService: 'DatabaseService',
  table: TodoSchema,
})
export class TodoRepository extends BaseRepository<TodoSchemaType> {
  // this.db is automatically injected and type-safe
  public async getTodos(): Promise<Todo[]> {
    return await this.db!.select().from(TodoSchema);
  }
}

🌟 Application Features

Todo Application

  • βœ… Full CRUD operations
  • βœ… User-specific todos
  • βœ… Authentication required
  • βœ… Request validation with Zod
  • βœ… UUID-based identification

Chat Application

REST API:

  • βœ… Create/list/delete chat rooms
  • βœ… Send/retrieve messages
  • βœ… Mark messages as read
  • βœ… User authorization

WebSocket (Authenticated):

  • βœ… Real-time messaging
  • βœ… Room-based communication
  • βœ… Message persistence
  • βœ… Typing indicators
  • βœ… Pub/Sub pattern

WebSocket (Public):

  • βœ… Public broadcast system
  • βœ… Channel subscriptions
  • βœ… Real-time notifications
  • βœ… No authentication needed

πŸ’Ύ Database

Using Drizzle ORM with automatic migrations:

// Define schema
export const TodoSchema = pgTable('todos', {
  id: uuid('id').primaryKey().defaultRandom(),
  title: text('title').notNull(),
  userId: uuid('user_id').references(() => UserSchema.id, { onDelete: 'cascade' }),
});
// Drizzle Kit generates migrations automatically
bun run drizzle:generate
bun run drizzle:migrate

Database Schema:

  • Users table (UUID primary keys)
  • Todos table (with foreign keys)
  • Chat rooms table
  • Messages table

πŸ§ͺ Testing

Interactive WebSocket Test Page: Visit http://localhost:3000/websocket-test.html to:

  • Test both WebSocket endpoints
  • Subscribe to channels
  • Send/receive messages
  • See real-time updates

πŸ“š Learn More

πŸ› οΈ Technologies

  • AsenaJS - Backend framework
  • TypeScript - Type safety
  • Drizzle ORM - Database toolkit
  • PostgreSQL - Database
  • Bun - JavaScript runtime
  • Hono - Web framework adapter
  • Zod - Schema validation

πŸ“ Notes

  • All IDs use UUID for scalability
  • WebSocket connections support authentication
  • Repository pattern with dependency injection
  • Clean separation of concerns
  • Full TypeScript type safety

Built with ❀️ using AsenaJS

This example demonstrates AsenaJS's powerful features for building modern, scalable applications with clean architecture and type safety.