A controller groups related routes. Mark the class with @ApiController and its
methods with a route decorator — @Get, @Post, @Put, @Patch, @Delete.
npx avleon make:controller welcome
src/controllers/welcome.controller.ts
import { ApiController, Get } from '@avleon/core';
@ApiController('/welcome')
export class WelcomeController {
@Get()
sayHello() {
return 'Hello';
}
}
Register it on the app:
app.ts
import { Avleon } from '@avleon/core';
import { WelcomeController } from './controllers/welcome.controller';
const app = Avleon.createApplication();
app.useControllers([WelcomeController]);
export default app;
@ApiController may be used bare, in which case routes mount at the root:
@ApiController
export class WelcomeController {}
| Decorator | Reads from |
|---|
@Param('id') | Route path segment |
@Query() | Query string |
@Body() | Request body |
@Header('x-token') | Request header |
@AuthUser() | The authenticated user |
@Get('/:id')
findOne(@Param('id') id: number) {
return { id };
}
Collection holds items in memory — useful for prototypes and tests, with no
database required.
npx avleon make:controller users --model user --rest
or shorthand
npx avleon m:c users -m user -r
users.controller.ts
models/user.ts
import {
ApiController,
Get,
Post,
Put,
Delete,
Param,
Body,
Collection,
HttpExceptions,
} from '@avleon/core';
import { User } from '../models/user';
@ApiController('/users')
export class UsersController {
private readonly users = Collection.from<User>([
{ id: 1, name: 'test 1', age: 20, active: true },
{ id: 2, name: 'test 2', age: 45, active: true },
{ id: 3, name: 'test 3', age: 38, active: false },
]);
@Get()
findAll() {
return this.users.find();
}
@Get('/:id')
findOne(@Param('id') id: number) {
const user = this.users.findOne((u) => u.id === id);
if (!user) throw HttpExceptions.notFound('User not found');
return user;
}
@Post()
create(@Body() createBody: Partial<User>) {
const lastId = this.users.max('id');
return this.users.add({ ...createBody, id: lastId + 1 });
}
@Put('/:id')
update(@Param('id') id: number, @Body() updateBody: Partial<User>) {
const user = this.users.findOne((u) => u.id === id);
if (!user) throw HttpExceptions.notFound('User not found');
this.users.update((u) => u.id === id, updateBody);
return 'Updated Successfully';
}
@Delete('/:id')
delete(@Param('id') id: number) {
const user = this.users.findOne((u) => u.id === id);
if (!user) throw HttpExceptions.notFound('User not found');
this.users.delete((u) => u.id === id);
return 'Deleted Successfully';
}
}
export class User {
id: number;
name: string;
age: number;
active: boolean;
}
Collection also offers paginate({ take, skip }), max, min, sum, avg,
updateBy, deleteBy and async variants (findAsync, findOneAsync).
With --orm the generated controller delegates to a service holding a TypeORM
repository, which keeps data access out of the controller.
npx avleon make:controller users --model user --rest --orm
or shorthand
npx avleon m:c users -m user -r --orm
users.controller.ts
services/user.service.ts
models/user.ts
import {
ApiController,
Get,
Post,
Put,
Delete,
Param,
Query,
Body,
HttpExceptions,
} from '@avleon/core';
import { UserService } from '../services/user.service';
import { User } from '../models/user';
@ApiController('/users')
export class UsersController {
constructor(private readonly userService: UserService) {}
@Get()
async index(@Query() query?: any) {
if (query && query.page) {
return this.userService.paginate(Number(query.page));
}
return this.userService.findAll();
}
@Get('/:id')
async findOne(@Param('id') id: number) {
const user = await this.userService.findByPk(id);
if (!user) throw HttpExceptions.notFound('User not found');
return user;
}
@Post()
async create(@Body() body: Partial<User>) {
return this.userService.create(body);
}
@Put('/:id')
async update(@Param('id') id: number, @Body() updateBody: Partial<User>) {
const user = await this.userService.findByPk(id);
if (!user) throw HttpExceptions.notFound('User not found');
return this.userService.update(id, updateBody);
}
@Delete('/:id')
async delete(@Param('id') id: number) {
const user = await this.userService.findByPk(id);
if (!user) throw HttpExceptions.notFound('User not found');
return this.userService.delete(id);
}
}
import { AppService, InjectRepository } from '@avleon/core';
import { Repository } from 'typeorm';
import { User } from '../models/user';
@AppService
export class UserService {
constructor(
@InjectRepository(User)
private readonly userRepository: Repository<User>,
) {}
findAll() {
return this.userRepository.find();
}
async paginate(page = 1, take = 10) {
const skip = page > 1 ? (page - 1) * take : 0;
const [data, total] = await this.userRepository.findAndCount({ take, skip });
return { total, totalPage: Math.ceil(total / take), current: page, data };
}
findByPk(id: number) {
return this.userRepository.findOneBy({ id });
}
create(body: Partial<User>) {
const user = this.userRepository.create(body);
return this.userRepository.save(user);
}
async update(id: number, body: Partial<User>) {
const result = await this.userRepository.update({ id }, body);
return (result.affected ?? 0) > 0;
}
async delete(id: number) {
const result = await this.userRepository.delete({ id });
return (result.affected ?? 0) > 0;
}
}
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity({ name: 'user' })
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column()
name: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
}
See Database for connecting the datasource.
HttpExceptions covers the common status codes — notFound, badRequest,
unauthorized, forbidden, internalError — and each takes an optional
message:
throw HttpExceptions.notFound('User not found');