CLI Examples ​
This guide walks you through creating your first Asena project from scratch, including creating components and building for production.
Getting asena onto your PATH.
Scaffolding with the interactive prompts.
03What the generated project looks like.
04Generating and filling in your first routes.
05Moving the logic out of the controller.
06Bundling the app with asena build.
Prerequisites ​
- Bun runtime v1.4 or higher
- Asena CLI installed globally
Verify installations:
bun --version
asena --versionStep 1: Install Asena CLI ​
If you haven't installed the CLI yet:
bun install -g @asenajs/asena-cliVerify installation:
asena --versionStep 2: Create a New Project ​
Create a new Asena project with interactive prompts:
asena createAnswer the prompts:
✔ Enter your project name: my-asena-app
✔ Select adapter: Ergenecore
✔ Do you want to setup logger? Yes
✔ 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
SSH or CI/CD Environment?
If you're working over SSH or in a CI/CD pipeline, interactive prompts won't work. Use non-interactive mode instead:
# Create project with all options via CLI arguments
asena create my-asena-app --adapter=ergenecore --logger --eslint --prettierStep 3: Verify Project Setup ​
Navigate to your project directory:
cd my-asena-appStart the development server:
asena dev startYou 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 3000Test the default endpoint:
curl http://localhost:3000/You should see: Hello asena
Step 4: Create a Controller ​
Generate a new controller:
asena g cEnter the controller name when prompted:
✔ Enter controller name: UserThis creates src/controllers/UserController.ts. Let's modify it:
import { Controller } from '@asenajs/asena/decorators';
import { Get } from '@asenajs/asena/decorators/http';
import type { Context } from '@asenajs/ergenecore';
@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' }
});
}
}import { Controller } from '@asenajs/asena/decorators';
import { Get } from '@asenajs/asena/decorators/http';
import type { Context } from '@asenajs/hono-adapter';
@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 startYou 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/1Controller 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 sEnter the service name:
✔ Enter service name: UserEdit src/services/UserService.ts:
import { Service } from '@asenajs/asena/decorators';
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/decorators';
import { Get, Post } from '@asenajs/asena/decorators/http';
import { Inject } from '@asenajs/asena/decorators/ioc';
import type { Context } from '@asenajs/ergenecore';
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<{ name: string; email: string }>();
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 buildOutput:
Build completed successfully.
Output: dist/index.asena.jsThe build files are in the dist/ directory (configured in asena-config.ts).
Run the production build:
bun dist/index.asena.jsBuild Output
The CLI automatically discovers and bundles all your components. No manual imports needed!
Step 7: Add Middleware (Optional) ​
Generate a middleware:
asena g mEnter the middleware name:
✔ Enter middleware name: LoggerEdit src/middlewares/LoggerMiddleware.ts:
import { Middleware } from '@asenajs/asena/decorators';
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.req.method;
const url = context.req.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({ path: '/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.jsonRelated ​
- CLI Commands - All CLI commands
- CLI Configuration - Configure your project
- Get Started Guide - Complete guide
- Adapters - Learn about adapters
- Controllers - Controller patterns
- Services - Service patterns
- Middleware - Middleware patterns
- WebSocket - WebSocket namespaces
- Database integration - Repositories with Drizzle ORM
- Deployment - Running in production