diff --git a/src/modules/database/constants.ts b/src/modules/database/constants.ts new file mode 100644 index 0000000..b3495e8 --- /dev/null +++ b/src/modules/database/constants.ts @@ -0,0 +1 @@ +export const CUSTOM_REPOSITORY_METADATA = 'CUSTOM_REPOSITORY_METADATA'; diff --git a/src/modules/database/database.module.ts b/src/modules/database/database.module.ts index 40024b2..fce3156 100644 --- a/src/modules/database/database.module.ts +++ b/src/modules/database/database.module.ts @@ -1,5 +1,9 @@ -import { DynamicModule, Module } from '@nestjs/common'; -import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; +import { DynamicModule, Module, Provider, Type } from '@nestjs/common'; +import { getDataSourceToken, TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm'; + +import { DataSource, ObjectType } from 'typeorm'; + +import { CUSTOM_REPOSITORY_METADATA } from '@/modules/database/constants'; @Module({}) export class DatabaseModule { @@ -10,4 +14,29 @@ export class DatabaseModule { imports: [TypeOrmModule.forRoot(configRegister())], }; } + static forRepository>( + repositories: T[], + datasourceName?: string, + ): DynamicModule { + const providers: Provider[] = []; + for (const Repository of repositories) { + const entity = Reflect.getMetadata(CUSTOM_REPOSITORY_METADATA, Repository); + if (!entity) { + continue; + } + providers.push({ + inject: [getDataSourceToken(datasourceName)], + provide: Repository, + useFactory: (datasource: DataSource): InstanceType => { + const base = datasource.getRepository>(entity); + return new Repository(base.target, base.manager, base.queryRunner); + }, + }); + } + return { + exports: providers, + module: DatabaseModule, + providers, + }; + } } diff --git a/src/modules/database/decorators/repository.decorator.ts b/src/modules/database/decorators/repository.decorator.ts new file mode 100644 index 0000000..f0f3832 --- /dev/null +++ b/src/modules/database/decorators/repository.decorator.ts @@ -0,0 +1,7 @@ +import { SetMetadata } from '@nestjs/common'; +import { ObjectType } from 'typeorm'; + +import { CUSTOM_REPOSITORY_METADATA } from '@/modules/database/constants'; + +export const CustomRepository = (entity: ObjectType): ClassDecorator => + SetMetadata(CUSTOM_REPOSITORY_METADATA, entity); diff --git a/src/modules/database/utils.ts b/src/modules/database/utils.ts new file mode 100644 index 0000000..8bad240 --- /dev/null +++ b/src/modules/database/utils.ts @@ -0,0 +1,32 @@ +import { isNil } from 'lodash'; +import { ObjectLiteral, SelectQueryBuilder } from 'typeorm'; + +import { PaginateOptions, PaginateReturn } from '@/modules/database/types'; + +export const paginate = async ( + qb: SelectQueryBuilder, + options: PaginateOptions, +): Promise> => { + const limit = isNil(options.limit) || options.limit < 1 ? 1 : options.limit; + const page = isNil(options.page) || options.page < 1 ? 1 : options.page; + const start = page >= 1 ? page - 1 : 0; + const totalItems = await qb.getCount(); + qb.take(limit).skip(start * limit); + const items = await qb.getMany(); + const totalPages = + totalItems % limit === 0 + ? Math.floor(totalItems / limit) + : Math.floor(totalItems / limit) + 1; + const remainder = totalItems % limit === 0 ? limit : totalItems % limit; + const itemCount = page < totalPages ? limit : remainder; + return { + items, + meta: { + totalItems, + itemCount, + perPage: limit, + totalPages, + currentPage: page, + }, + }; +};