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:migrate
bun run build:start
Server 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: Cookie
Create 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:migrate
Database 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.