I am trying to figure out a way to setup migrations with typeOrm 0.3.12 in Nestjs along with ConfigModule.The database to be used in this project is Postgres. I have two problems:
What I tried in each case:
import { Entity, PrimaryGeneratedColumn, Column} from 'typeorm';
@Entity()
export class DoctorEntity {
@PrimaryGeneratedColumn()
id:number;
@Column()
fullname: string;
}
which I import in its corresponding module like this:
@Module({
imports: [TypeOrmModule.forFeature([DoctorEntity])],
controllers: [DoctorsController],
providers: [DoctorsService]
})
export class DoctorsModule {}
My App Module is the following:
@Module({
imports: [ConfigModule.forRoot({
isGlobal: true,
envFilePath: `.env.${process.env.NODE_ENV}`
}),
TypeOrmModule.forRoot(config),
DoctorsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
And the config I use is in a file called ormconfig.ts in my src directory of the project, where I also create and export default a data source because it is needed for the migration scripts. Please note that I have tried many different approaches in entities field eg __dirname+'/**/*.entity{.ts,.js}'
but with no luck. The ormconfig is the following:
export const config : DataSourceOptions = {
type:"postgres",
host: "localhost",
port: 5432,
username: "test",
password: "123",
database: "test",
entities: ['dist/**/*.entity.js'],
synchronize: false,
migrations: ['./migrations/*.js']
}
const dataSource = new DataSource(config);
export default dataSource;
In my packages.json I have the following scripts regarding the migrations:
"typeorm": "nest build && typeorm-ts-node-commonjs -d ./dist/ormconfig.js",
"migration:generate": "npm run typeorm migration:generate",
"schema:log": "npm run typeorm schema:log"
If i put the DoctorEntity directly inside the entities field in ormconfig, it is working fine but when I use glob pattern I always get the following error:
No changes in database schema were found - cannot generate a migration. To create a new empty migration use "typeorm migration:create" command
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'mysql',
host: configService.get('HOST'),
port: +configService.get('PORT'),
username: configService.get('USERNAME'),
password: configService.get('PASSWORD'),
database: configService.get('DATABASE'),
entities: [],
synchronize: true,
}),
inject: [ConfigService],
});
I would still need the ormconfig.ts though from which I export the data source needed by the migration scripts, but I do not have access to the ConfigService there and I am feeling kinda lost... I have spent 3 days searching for a solution to problem but with no luck. So, I thought that it would be "ok" to just pass the entities hardcoded but when I tried to continue I stumbled upon the 2nd problem. Any help will be greatly appreciated. Thanks!
Regarding the first part of the problem, I came across this github issue for typeorm 0.3.12 sprecifically. It appears that the 0.3.12 version is using a version of glob library that is causing the problem. The problem only exists on Windows
.The way to fix it:
As of current date, downgrading typeorm to version 0.3.11 solves the problem of not being able to load entities with glob pattern path
. Since there is an open issue on github, I suppose this will be fixed in next version.
UPDATE: About the second part of the question, there really is no elegant way to do it. The database connection options will have to be passed through TypeOrmModule.forRootAsync as shown below:
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => ({
type: 'mysql',
host: configService.get('HOST'),
port: +configService.get('PORT'),
username: configService.get('USERNAME'),
password: configService.get('PASSWORD'),
database: configService.get('DATABASE'),
entities: [],
synchronize: true,
}),
inject: [ConfigService],
});
At the same time there should exist a separate config file that will instantiate a DataSource
and export it as default, so that the migration scripts can work. In order to load the variables from the env file - using directly the dotenv
library, we will open the file and read it with fs
and then pass them manually to the DataSourceOptions
object.
Something like the following:
import * as dotenv from 'dotenv';
import * as fs from 'fs';
const data: any = dotenv.parse(fs.readFileSync(`${environment}.env`));
export const config : DataSourceOptions = {
type:"postgres",
host: data.HOST,
port: data.PORT,
username: data.USERNAME,
password: data.PASSWORD,
database: data.DATABASE,
entities: ['dist/**/*.entity.js'],
synchronize: false,
migrations: ['./migrations/*.js']
}
export default new DataSource(config);