chore(repo) init AppDatabase module
This commit is contained in:
parent
9f35377481
commit
0d60b73816
@ -1,11 +1,12 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AppConfigModule } from './modules/config/config.module';
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { AppConfigModule } from './modules/config/config.module';
|
||||
import { AppDatabaseModule } from './modules/database/database.module';
|
||||
import { AppLoggerModule } from './modules/logger/logger.module';
|
||||
|
||||
@Module({
|
||||
imports: [AppConfigModule, AppLoggerModule],
|
||||
imports: [AppConfigModule, AppLoggerModule, AppDatabaseModule],
|
||||
controllers: [AppController],
|
||||
providers: [AppService],
|
||||
})
|
||||
|
63
apps/ebitemp-api/src/app/config/database.config.ts
Normal file
63
apps/ebitemp-api/src/app/config/database.config.ts
Normal file
@ -0,0 +1,63 @@
|
||||
import { registerAs } from '@nestjs/config';
|
||||
import { ConnectionString } from 'connection-string';
|
||||
import { first } from 'lodash';
|
||||
import { z } from 'zod';
|
||||
import coerceRecordTypes from '../modules/config/coerce-record-types';
|
||||
|
||||
export const databaseSchema = z.object({
|
||||
connectionString: z.string(),
|
||||
type: z.enum([
|
||||
'mysql',
|
||||
'postgres',
|
||||
'cockroachdb',
|
||||
'sap',
|
||||
'mariadb',
|
||||
'sqlite',
|
||||
'cordova',
|
||||
'react-native',
|
||||
'nativescript',
|
||||
'sqljs',
|
||||
'oracle',
|
||||
'mssql',
|
||||
'mongodb',
|
||||
'aurora-mysql',
|
||||
'aurora-postgres',
|
||||
'expo',
|
||||
'better-sqlite3',
|
||||
'capacitor',
|
||||
'spanner',
|
||||
]),
|
||||
host: z.string(),
|
||||
port: z.number().optional(),
|
||||
username: z.string(),
|
||||
password: z.string(),
|
||||
database: z.string(),
|
||||
secure: z.boolean(),
|
||||
});
|
||||
|
||||
export type DatabaseConfig = z.TypeOf<typeof databaseSchema>;
|
||||
const rawDatabaseSchema = z.object({
|
||||
connectionString: z.string(),
|
||||
secure: z.boolean().default(true),
|
||||
});
|
||||
|
||||
export const databaseConfig = registerAs('database', () => {
|
||||
const env = coerceRecordTypes(process.env);
|
||||
const envParsed = rawDatabaseSchema.strict().parse({
|
||||
connectionString: env['DATABASE_CONNECTION_STRING'],
|
||||
secure: env['DATABASE_SECURE'],
|
||||
});
|
||||
|
||||
const connectionString = new ConnectionString(envParsed.connectionString);
|
||||
const config: DatabaseConfig = databaseSchema.strict().parse({
|
||||
connectionString: connectionString.toString(),
|
||||
type: connectionString.protocol,
|
||||
host: first(connectionString.hosts)?.name,
|
||||
port: first(connectionString.hosts)?.port,
|
||||
username: connectionString.user,
|
||||
password: connectionString.password,
|
||||
database: first(connectionString.path),
|
||||
secure: envParsed.secure,
|
||||
});
|
||||
return config;
|
||||
});
|
@ -3,13 +3,14 @@ import { Module } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { localConfig } from '../../config/local.config';
|
||||
import { loggerConfig } from '../../config/logger.config';
|
||||
import { databaseConfig } from '../../config/database.config';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
expandVariables: true,
|
||||
load: [localConfig, loggerConfig],
|
||||
load: [localConfig, loggerConfig, databaseConfig],
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
@ -0,0 +1 @@
|
||||
export const APP_DATASOURCES = Symbol('APP_DATASOURCES');
|
40
apps/ebitemp-api/src/app/modules/database/database.module.ts
Normal file
40
apps/ebitemp-api/src/app/modules/database/database.module.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { DataSource } from 'typeorm';
|
||||
import { APP_DATASOURCES } from './database.constants';
|
||||
import { DatabaseConfig, databaseConfig } from '../../config/database.config';
|
||||
import { typeormTransactionalDataSourceFactory } from './typeorm-data-source-factory';
|
||||
import { typeormModuleOptionsFactory } from './typeorm-module-options-factory';
|
||||
import { typeormEntitiesFromImport } from './typeorm-import-entities';
|
||||
|
||||
const dataSources: DataSource[] = [];
|
||||
|
||||
const typeormModules = [
|
||||
TypeOrmModule.forRootAsync({
|
||||
name: 'ebitemp-api',
|
||||
dataSourceFactory: typeormTransactionalDataSourceFactory('ebitemp-api', dataSources),
|
||||
useFactory: async (dbConfig: DatabaseConfig) => {
|
||||
// const entities = await import('./path/to/entities');
|
||||
const entities = {};
|
||||
|
||||
const config = await typeormModuleOptionsFactory('ebitemp-api', dbConfig, [
|
||||
typeormEntitiesFromImport(entities),
|
||||
]);
|
||||
return config;
|
||||
},
|
||||
inject: [databaseConfig.KEY],
|
||||
}),
|
||||
];
|
||||
|
||||
const dataSourcesProvider = {
|
||||
provide: APP_DATASOURCES,
|
||||
useValue: dataSources,
|
||||
};
|
||||
|
||||
@Global()
|
||||
@Module({
|
||||
imports: [...typeormModules],
|
||||
providers: [dataSourcesProvider],
|
||||
exports: [...typeormModules, dataSourcesProvider],
|
||||
})
|
||||
export class AppDatabaseModule {}
|
@ -0,0 +1,15 @@
|
||||
import { DataSource, DataSourceOptions } from 'typeorm';
|
||||
|
||||
export const typeormDataSourceFactory = (dataSources: DataSource[]) => async (options?: DataSourceOptions) => {
|
||||
const dataSource = await new DataSource(options!).initialize();
|
||||
dataSources.push(dataSource);
|
||||
return dataSource;
|
||||
};
|
||||
|
||||
export const typeormTransactionalDataSourceFactory =
|
||||
(name: string, dataSources: DataSource[]) => async (options?: DataSourceOptions) => {
|
||||
const tt = await import('typeorm-transactional');
|
||||
const dataSource = tt.addTransactionalDataSource({ name: name, dataSource: new DataSource(options!) });
|
||||
dataSources.push(dataSource);
|
||||
return dataSource;
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
export const typeormEntitiesFromImport = async <T>(entities: T) => {
|
||||
return (Object.keys(entities) as Array<keyof typeof entities>).map(
|
||||
(entity: keyof typeof entities) => entities[entity]
|
||||
);
|
||||
};
|
@ -0,0 +1,24 @@
|
||||
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||
import { DatabaseConfig } from '../../config/database.config';
|
||||
|
||||
export const typeormModuleOptionsFactory = async (
|
||||
name: string,
|
||||
databaseConfig: DatabaseConfig,
|
||||
entities: any[]
|
||||
): Promise<TypeOrmModuleOptions> => {
|
||||
return {
|
||||
name: name,
|
||||
type: databaseConfig.type as any,
|
||||
host: databaseConfig.host,
|
||||
port: databaseConfig.port,
|
||||
username: databaseConfig.username,
|
||||
password: databaseConfig.password,
|
||||
database: databaseConfig.database,
|
||||
entities: [...entities],
|
||||
synchronize: false,
|
||||
logging: process.env['NODE_ENV'] !== 'production',
|
||||
options: {
|
||||
...(!databaseConfig.secure ? { trustServerCertificate: true } : {}),
|
||||
}
|
||||
};
|
||||
};
|
@ -0,0 +1,5 @@
|
||||
|
||||
export async function patchTypeOrm() {
|
||||
(await import('typeorm-transactional')).initializeTransactionalContext();
|
||||
(await import('typeorm-scoped')).patchSelectQueryBuilder();
|
||||
};
|
@ -3,14 +3,17 @@
|
||||
* This is only a minimal backend to get started.
|
||||
*/
|
||||
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import { Logger as PinoLogger } from 'nestjs-pino';
|
||||
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app/app.module';
|
||||
import { LocalConfig, localConfig } from './app/config/local.config';
|
||||
import { patchTypeOrm } from './app/modules/database/typeorm-patch';
|
||||
|
||||
async function bootstrap() {
|
||||
await patchTypeOrm();
|
||||
|
||||
const appOpts = (() => {
|
||||
const loggerOpts = { bufferLogs: true };
|
||||
return {
|
||||
|
@ -10,13 +10,16 @@
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"10": "^0.0.1",
|
||||
"@autotelic/pino-seq-transport": "^0.1.0",
|
||||
"@nestjs/common": "^10.0.2",
|
||||
"@nestjs/config": "^4.0.0",
|
||||
"@nestjs/core": "^10.0.2",
|
||||
"@nestjs/platform-express": "^10.0.2",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"@nx/devkit": "20.4.1",
|
||||
"axios": "^1.6.0",
|
||||
"connection-string": "^4.4.0",
|
||||
"lodash": "^4.17.21",
|
||||
"nestjs-pino": "^4.3.0",
|
||||
"patch-package": "^8.0.0",
|
||||
@ -24,6 +27,9 @@
|
||||
"pino-pretty": "^13.0.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.8.0",
|
||||
"typeorm": "^0.3.20",
|
||||
"typeorm-scoped": "^1.2.0",
|
||||
"typeorm-transactional": "^0.5.0",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
307
pnpm-lock.yaml
generated
307
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user