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()exportclassTodoService{
@Inject("TodoRepository")// @Inject(TodoRepository) you can use like that alsoprivatetodoRepository: TodoRepository;}@Controller({path: '/todos',middlewares: [AuthMiddleware]})exportclassTodoController{
@Get({path: '/',description: 'Get all todos'})publicasyncgetTodos(context: Context){}}import{BunSQLDatabase}from"drizzle-orm/bun-sql";
@Repository({databaseService: 'DatabaseService',table: TodoSchema,})exportclassTodoRepositoryextendsBaseRepository<TodoSchemaType,BunSQLDatabase>{publicasyncgetTodosByUserId(userId: string): Promise<Todo[]>{returnawaitthis.db!.select().from(TodoSchema);}}@WebSocket({path: 'chat-room',middlewares: [WSAuthMiddleware]})exportclassChatWebSocketextendsAsenaWebSocketService<{user: User}>{
@Inject("ChatService")privatechatService: ChatService;publicasynconMessage(ws: Socket,message: Buffer|string){// Real-time message handling}}@Middleware()exportclassAuthMiddlewareimplementsMiddlewareService{publicasynchandle(context: Context,next: Next){// Authentication logic}}@Middleware({validator: true})exportclassCreateTodoValidatorextendsValidationService{publicjson(){returnz.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 cookieconstchatWs=newWebSocket('ws://localhost:3000/chat-room');chatWs.onopen=()=>{// Join a room and send messagechatWs.send(JSON.stringify({type: 'message',content: 'Hello everyone!',roomId: 'room-uuid'}));};chatWs.onmessage=(event)=>{constdata=JSON.parse(event.data);console.log('Received:',data);};// No authentication requiredconstnotifWs=newWebSocket('ws://localhost:3000/notifications');notifWs.onopen=()=>{// Subscribe to a channelnotifWs.send(JSON.stringify({type: 'subscribe',channel: 'updates'}));};notifWs.onmessage=(event)=>{constnotification=JSON.parse(event.data);console.log('Notification:',notification);};AsenaJS uses a powerful IoC container. Dependencies are automatically resolved:
@Service()exportclassChatService{
@Inject(ChatRepository)// Automatically injectedprivatechatRepository: 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})exportclassTodoController{
@Post({path: '/',validator: CreateTodoValidator// Route-specific})publicasynccreate(context: Context){}}WebSockets can use middlewares just like REST endpoints:
@WebSocket({path: 'chat-room',middlewares: [WSAuthMiddleware]// Auth required})exportclassChatWebSocketextendsAsenaWebSocketService<{user: User}>{publiconMessage(ws: Socket<{user: User}>,message: Buffer|string){// User data available from ws.data.user}}Using Drizzle ORM with AsenaJS:
@Repository({databaseService: 'DatabaseService',table: TodoSchema,})exportclassTodoRepositoryextendsBaseRepository<TodoSchemaType>{// this.db is automatically injected and type-safepublicasyncgetTodos(): Promise<Todo[]>{returnawaitthis.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 schemaexportconstTodoSchema=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.