A comprehensive example application showcasing AsenaJS framework features through Todo and Chat applications with real-time WebSocket communication.
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
@Service()
export class TodoService {
@Inject("TodoRepository")
// @Inject(TodoRepository) you can use like that also
private todoRepository: TodoRepository;
}@Controller({ path: '/todos', middlewares: [AuthMiddleware] })
export class TodoController {
@Get({ path: '/', description: 'Get all todos' })
public async getTodos(context: Context) { }
}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);
}
}@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
}
}@Middleware()
export class AuthMiddleware implements MiddlewareService {
public async handle(context: Context, next: Next) {
// Authentication logic
}
}@Middleware({ validator: true })
export class CreateTodoValidator extends ValidationService {
public json() {
return z.object({
title: z.string(),
description: z.string(),
isCompleted: z.boolean(),
});
}
}┌─────────────────────────────────────┐
│ Controllers │ ← REST & WebSocket endpoints
│ @Controller, @WebSocket │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Services │ ← Business logic
│ @Service │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Repositories │ ← Data access
│ @Repository (BaseRepository) │
└──────────────┬──────────────────────┘
│
┌──────────────▼──────────────────────┐
│ Database (Drizzle ORM) │
└─────────────────────────────────────┘
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
bun install# Create PostgreSQL database
psql -U postgres -c "CREATE DATABASE asenatest;"
# Generate migrations from schemas
bun run drizzle:generate
# Apply migrations
bun run drizzle:migratebun run build:startServer runs on http://localhost:3000
Open http://localhost:3000/websocket-test.html for interactive WebSocket testing.
Create Todo:
POST /todos
Authorization: Cookie
{
"title": "Learn AsenaJS",
"description": "Study the framework",
"isCompleted": false
}Get All Todos:
GET /todos
Authorization: CookieCreate Chat Room:
POST /chat/rooms
{
"name": "General",
"description": "Main chat room"
}Send Message:
POST /chat/messages
{
"content": "Hello, World!",
"roomId": "room-uuid"
}// 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);
};// 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);
};AsenaJS uses a powerful IoC container. Dependencies are automatically resolved:
@Service()
export class ChatService {
@Inject(ChatRepository) // Automatically injected
private chatRepository: ChatRepository;
}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
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) { }
}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
}
}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);
}
}- ✅ Full CRUD operations
- ✅ User-specific todos
- ✅ Authentication required
- ✅ Request validation with Zod
- ✅ UUID-based identification
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
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:migrateDatabase Schema:
- Users table (UUID primary keys)
- Todos table (with foreign keys)
- Chat rooms table
- Messages table
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
- AsenaJS - Backend framework
- TypeScript - Type safety
- Drizzle ORM - Database toolkit
- PostgreSQL - Database
- Bun - JavaScript runtime
- Hono - Web framework adapter
- Zod - Schema validation
- 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.