Configuration
Configuration is used for creating a third-party plugin for Abyss.ts.
By implementing IAbyssalConfiguration
, we can create a plugin like MikroORM
, TypeORM
, ... and apply it for Abyss.ts. The configuration uses AsyncDisposable
to clean up resource when the application is exiting.
NOTE
Always remember to clean up your configuration resources to reduce the memory leak.
Create a configuration
To create a configuration, we will create a class that will implement interface IAbyssalConfiguration
.
import { glob } from 'glob';
import { cwd } from 'node:process';
import { resolve } from 'node:path';
import { existsSync } from 'node:fs';
import { MikroORM, type Options } from '@mikro-orm/core';
import { pushToIoCContainer, type IAbyssalConfiguration } from '@abyss.ts/core';
import { EM, ORM } from './constants';
function resolveFilePaths(paths: string[]): string[] {
return paths.map((path) => {
return resolve(cwd(), path);
});
}
export class MikroOrmConfiguration implements IAbyssalConfiguration {
#orm?: MikroORM;
async create(): Promise<void> {
let config: Options;
const filePaths = await glob(
resolveFilePaths(['**/mikro-orm.config.ts', '**/mikro-orm.config.js']),
{
ignore: ['node_modules/**'],
},
);
const filePath = filePaths[0] || '';
if (existsSync(filePath)) {
config = (await import(filePath)).default;
} else {
throw 'Cannot find `mikro-orm.config.ts` or `mikro-orm.config.js`';
}
this.#orm = await MikroORM.init({
baseDir: cwd(),
tsNode: filePath.includes('.ts'),
...config,
});
pushToIoCContainer({
key: ORM,
instance: this.#orm,
});
pushToIoCContainer({
key: EM,
instance: this.#orm.em,
});
}
async [Symbol.asyncDispose](): Promise<void> {
if (this.#orm) {
const isConnected = await this.#orm?.isConnected();
if (isConnected) {
await this.#orm.close();
}
}
}
}
A configuration has two methods: create
and Symbol.asyncDispose
.
create
method acts like a constructor, usually to initial and register services to the IoC.
Symbol.asyncDispose
is from new feature of TypeScript 5.2
called AsyncDisposable
. This is used for clean up resource (for the example above, we need to close the orm
), and is called when Node.js receives signal SIGINT
and SIGTERM
.
Apply configuration
Applying configuration simply calls chaining method from AbyssalApplication
, the application will auto create it automatically.
import { ExpressApplication } from '@abyss.ts/express-runner';
import { MikroOrmConfiguration, MikroOrmMiddleware } from '@abyss.ts/mikro-orm';
ExpressApplication.create()
.useBodyParser()
.usePort({
port: 4_000,
isStrict: true,
})
.useConfiguration(MikroOrmConfiguration)
.useMiddleware(MikroOrmMiddleware)
.run();
Use configuration's injection
Since the configuration registers ORM
and EM
to the IoC, we can inject those to the services/controllers and do the query.
@Injectable()
export class FilterService {
constructor(
@InjectRepository(Product)
private readonly productRepository: EntityRepository<Product>,
) {}
public call() {
return this.productRepository.findAll();
}
}
@Controller('products')
export class ProductsController {
constructor(
@Inject(FilterService) private readonly filterService: FilterService,
) {}
@Get()
async index() {
return this.filterService.call();
}
}