🚧 AvleonJs is in active development and not ready for production use.

Controller

Avleon Controller

A controller groups related routes. Mark the class with @ApiController and its methods with a route decorator — @Get, @Post, @Put, @Patch, @Delete.

Basic Controller

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 {}

Route parameters

DecoratorReads 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 };
}

REST controller with in-built collection

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';
  }
}

Collection also offers paginate({ take, skip }), max, min, sum, avg, updateBy, deleteBy and async variants (findAsync, findOneAsync).

REST controller with typeorm repository

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);
  }
}

See Database for connecting the datasource.

Throwing errors

HttpExceptions covers the common status codes — notFound, badRequest, unauthorized, forbidden, internalError — and each takes an optional message:

throw HttpExceptions.notFound('User not found');