CLI Examples
This guide walks you through creating your first Asena project from scratch, including creating components and building for production.
Prerequisites
- Bun runtime v1.2.8 or higher
- Asena CLI installed globally
Verify installations:
bun --version
asena --version
Step 1: Install Asena CLI
If you haven't installed the CLI yet:
bun install -g @asenajs/asena-cli
Verify installation:
asena --version
Step 2: Create a New Project
Create a new Asena project with interactive prompts:
asena create
Answer the prompts:
✔ Enter your project name: my-asena-app
✔ Select adapter: Ergenecore
✔ Do you want to setup ESLint? Yes
✔ Do you want to setup Prettier? Yes
⠙ Creating asena project...
Adapter Selection
- Ergenecore - Bun-native adapter with zero dependencies (recommended)
- Hono - Hono framework-based adapter with ecosystem compatibility
ESLint and Prettier
Setting up ESLint and Prettier is recommended for maintaining code quality and consistency.
Step 3: Verify Project Setup
Navigate to your project directory:
cd my-asena-app
Start the development server:
asena dev start
You should see output like this:
Build completed successfully.
2025-10-15 14:30:19 [info]:
___ _____ ______ _ __ ___
/ | / ___/ / ____// | / // |
/ /| | \__ \ / __/ / |/ // /| |
/ ___ | ___/ // /___ / /| // ___ |
/_/ |_|/____//_____//_/ |_//_/ |_|
2025-10-15 14:30:20 [info]: Adapter: ErgenecoreAdapter implemented
2025-10-15 14:30:20 [info]: All components registered and ready to use
2025-10-15 14:30:20 [info]: No configs found
2025-10-15 14:30:20 [info]: Controller: AsenaController found:
2025-10-15 14:30:20 [info]: Successfully registered GET route for PATH: /
2025-10-15 14:30:20 [info]: Controller: AsenaController successfully registered.
2025-10-15 14:30:20 [info]: No websockets found
2025-10-15 14:30:20 [info]: Server started on port 3000
Test the default endpoint:
curl http://localhost:3000/
You should see: Hello asena
Step 4: Create a Controller
Generate a new controller:
asena g c
Enter the controller name when prompted:
✔ Enter controller name: User
This creates src/controllers/UserController.ts
. Let's modify it:
For Ergenecore Adapter
import { Controller } from '@asenajs/asena/server';
import { Get } from '@asenajs/asena/web';
import type { Context } from '@asenajs/ergenecore/types';
@Controller('/users')
export class UserController {
@Get({ path: '/' })
async getAllUsers(context: Context) {
return context.send({
message: 'List of all users',
users: [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' }
]
});
}
@Get({ path: '/:id' })
async getUserById(context: Context) {
const id = context.getParam('id');
return context.send({
message: `User with ID: ${id}`,
user: { id, name: 'John Doe' }
});
}
}
For Hono Adapter
import { Controller } from '@asenajs/asena/server';
import { Get } from '@asenajs/asena/web';
import type { Context } from '@asenajs/hono-adapter/types';
@Controller('/users')
export class UserController {
@Get({ path: '/' })
async getAllUsers(context: Context) {
return context.send({
message: 'List of all users',
users: [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' }
]
});
}
@Get({ path: '/:id' })
async getUserById(context: Context) {
const id = context.getParam('id');
return context.send({
message: `User with ID: ${id}`,
user: { id, name: 'John Doe' }
});
}
}
Restart the development server:
asena dev start
You should see the new routes registered:
2025-10-15 14:35:27 [info]: Controller: UserController found:
2025-10-15 14:35:27 [info]: Successfully registered GET route for PATH: /users
2025-10-15 14:35:27 [info]: Successfully registered GET route for PATH: /users/:id
2025-10-15 14:35:27 [info]: Controller: UserController successfully registered.
Test the new endpoints:
# Get all users
curl http://localhost:3000/users
# Get user by ID
curl http://localhost:3000/users/1
Controller Names in Output
Controller names are visible in logs when buildOptions.minify.identifiers
is set to false
in asena.config.ts
.
Step 5: Create a Service
Generate a service for business logic:
asena g s
Enter the service name:
✔ Enter service name: User
Edit src/services/UserService.ts
:
import { Service } from '@asenajs/asena/server';
interface User {
id: number;
name: string;
email: string;
}
@Service()
export class UserService {
private users: User[] = [
{ id: 1, name: 'John Doe', email: 'john@example.com' },
{ id: 2, name: 'Jane Smith', email: 'jane@example.com' }
];
async getAllUsers(): Promise<User[]> {
return this.users;
}
async getUserById(id: number): Promise<User | undefined> {
return this.users.find(user => user.id === id);
}
async createUser(name: string, email: string): Promise<User> {
const newUser: User = {
id: this.users.length + 1,
name,
email
};
this.users.push(newUser);
return newUser;
}
}
Update your controller to use the service:
import { Controller } from '@asenajs/asena/server';
import { Get, Post } from '@asenajs/asena/web';
import { Inject } from '@asenajs/asena/ioc';
import type { Context } from '@asenajs/ergenecore/types';
import { UserService } from '../services/UserService';
@Controller('/users')
export class UserController {
@Inject(UserService)
private userService: UserService;
@Get({ path: '/' })
async getAllUsers(context: Context) {
const users = await this.userService.getAllUsers();
return context.send({ users });
}
@Get({ path: '/:id' })
async getUserById(context: Context) {
const id = parseInt(context.getParam('id'));
const user = await this.userService.getUserById(id);
if (!user) {
return context.send({ error: 'User not found' }, 404);
}
return context.send({ user });
}
@Post({ path: '/' })
async createUser(context: Context) {
const { name, email } = await context.getBody();
const user = await this.userService.createUser(name, email);
return context.send({ user }, 201);
}
}
Test the new endpoints:
# Get all users
curl http://localhost:3000/users
# Get user by ID
curl http://localhost:3000/users/1
# Create a new user
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name":"Bob Johnson","email":"bob@example.com"}'
Step 6: Build for Production
Build your project:
asena build
Output:
Build completed successfully.
Output: dist/index.js
The build files are in the dist/
directory (configured in asena.config.ts
).
Run the production build:
bun dist/index.js
Build Output
The CLI automatically discovers and bundles all your components. No manual imports needed!
Step 7: Add Middleware (Optional)
Generate a middleware:
asena g m
Enter the middleware name:
✔ Enter middleware name: Logger
Edit src/middlewares/LoggerMiddleware.ts
:
import { Middleware } from '@asenajs/asena/server';
import { MiddlewareService, type Context } from '@asenajs/ergenecore';
@Middleware()
export class LoggerMiddleware extends MiddlewareService {
async handle(context: Context, next: () => Promise<void>) {
const start = Date.now();
const method = context.getRequest().method;
const url = context.getRequest().url;
console.log(`[${method}] ${url} - Started`);
await next();
const duration = Date.now() - start;
console.log(`[${method}] ${url} - Completed in ${duration}ms`);
}
}
Apply it to your controller:
@Controller('/users', { middlewares: [LoggerMiddleware] })
export class UserController {
// ... your routes
}
Project Structure
Your project should now look like this:
my-asena-app/
├── src/
│ ├── controllers/
│ │ ├── AsenaController.ts # Default controller
│ │ └── UserController.ts # Your controller
│ ├── services/
│ │ └── UserService.ts # Your service
│ ├── middlewares/
│ │ └── LoggerMiddleware.ts # Your middleware
│ └── index.ts # Entry point
├── dist/ # Build output
├── asena.config.ts # CLI configuration
├── package.json
└── tsconfig.json
Next Steps
Now that you've created your first Asena application:
- Learn about Controllers
- Explore Services
- Understand Middleware
- Set up Database integration
- Configure WebSocket
- Deploy to Production
Related Documentation:
- CLI Commands - All CLI commands
- CLI Configuration - Configure your project
- Get Started Guide - Complete guide
- Adapters - Learn about adapters