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

Database Intregation

Connecting Avleon to a database with TypeORM or Knex.

Avleon ships with two database integrations: TypeORM for an entity/repository model, and Knex for a query builder. Both are optional peer dependencies — install the one you use, along with its driver.

TypeORM
Knex
npm i typeorm pg

TypeORM

Register the datasource on the app with useTypeORM. It is asynchronous, so await it before the app runs.

app.ts
import { Avleon } from '@avleon/core';
import { User } from './models/user';
import { UsersController } from './controllers/users.controller';

const app = Avleon.createApplication();

export async function setup() {
  await app.useTypeORM({
    type: 'postgres',
    host: 'localhost',
    port: 5432,
    username: 'postgres',
    password: 'postgres',
    database: 'myapi',
    entities: [User],
    synchronize: true, // development only
  });

  app.useControllers([UsersController]);
  return app;
}

export default app;
serve.ts
import { Environment, inject } from '@avleon/core';
import app, { setup } from './app';

async function serve() {
  await setup();
  const env = inject(Environment);
  await app.run(env.get<number>('PORT') || 4000);
}

serve();

Note: The project generated by avleon new compiles to CommonJS, where top-level await is not available. Wrap async setup in a function and await it from your entry point, as above.

Defining an entity

models/user.ts
import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn } from 'typeorm';

@Entity({ name: 'user' })
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @CreateDateColumn()
  createdAt: Date;
}

Generate one with the CLI:

npx avleon make:model user --orm

Injecting a repository

Use @InjectRepository to receive a TypeORM Repository in any service or controller:

services/user.service.ts
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();
  }

  findByPk(id: number) {
    return this.userRepository.findOneBy({ id });
  }

  create(body: Partial<User>) {
    const user = this.userRepository.create(body);
    return this.userRepository.save(user);
  }
}

Using a config class

Instead of an inline object, useTypeORM also accepts an @AppConfig class. This keeps credentials in one place and gives access to the environment:

config/database.config.ts
app.ts
import { AppConfig, AvleonConfig, Environment } from '@avleon/core';
import { DataSourceOptions } from 'typeorm';
import { User } from '../models/user';

@AppConfig
export class DatabaseConfig extends AvleonConfig<DataSourceOptions> {
  config(env: Environment): DataSourceOptions {
    return {
      type: 'postgres',
      url: env.get<string>('DATABASE_URL'),
      entities: [User],
    };
  }
}

Knex

Register the connection with useKnex, which takes a Knex config or an @AppConfig class just like useTypeORM:

app.ts
import { Avleon } from '@avleon/core';

const app = Avleon.createApplication();

export async function setup() {
  await app.useKnex({
    client: 'pg',
    connection: process.env.DATABASE_URL,
  });
  return app;
}

export default app;

Inject KnexDB to reach the query builder through its client property:

services/report.service.ts
import { AppService, KnexDB } from '@avleon/core';

@AppService
export class ReportService {
  constructor(private readonly db: KnexDB) {}

  activeUsers() {
    return this.db.client('user').where({ active: true }).select('*');
  }
}

KnexDB throws if it is resolved before useKnex has run, so always register the connection during app setup.

Without a database

For prototypes and tests, Collection gives the same shape of API over an in-memory array — see Controller for an example.