I have a project that's using nestjs
with typeorm
.
There's a database.config.ts
:
const entitiesPath = join(__dirname, '..', 'entities', '*.entity.{ts,js}');
const migrationsPath = join(__dirname, '..', 'migrations', '*.*');
const subscribersPath = join(__dirname, '..', 'subscribers', '*.subscriber.{ts,js}');
export const connectionConfig = {
name: 'default',
type: 'postgres',
host: process.env.DB_HOST,
port: process.env.DB_PORT,
database: process.env.DB_DATABASE,
schema: process.env.DB_SCHEMA,
username: process.env.DB_USERNAME,
password: process.env.DB_PASSWORD,
};
export const databaseConfig = registerAs('database', () => ({
entities: [entitiesPath],
migrations: [migrationsPath],
subscribers: [subscribersPath],
...connectionConfig,
}));
database.module.ts
:
const connectionProvider = {
provide: DatabaseProvider.Connection,
useFactory: async (configService: ConfigService): Promise<Connection> => {
const databaseConfig = configService.get('database');
return await createConnection({
...databaseConfig,
});
},
inject: [ConfigService],
};
@Module({
imports: [
ConfigModule.forRoot({
load: [databaseConfig],
}),
],
providers: [connectionProvider, TestRepository],
exports: [connectionProvider, TestRepository],
})
export class DatabaseModule {}
TestRepository
is a class that extends a BaseRepository
with a unit of work almost like described here.
The connection is injected in it like this:
constructor(@Inject(DatabaseProvider.Connection) private readonly conn: Connection) {
super(conn);
}
In the base repository I create the QueryRunner
in the constructor:
constructor(connection: Connection) {
super();
this.connection = connection;
this.unitOfWorkQueryRunner = this.connection.createQueryRunner();
}
Now, I want to write some integration test for the unit of work and I am getting the connection and TestRepository
like this:
describe('test.repository.ts', () => {
let app: INestApplication;
let connection: Connection;
let testRepository: TestRepository;
beforeAll(async () => {
app = await NestFactory.create(DatabaseModule);
connection = app.get<Connection>(DatabaseProvider.Connection);
});
beforeEach(async () => {
await connection.runMigrations();
testRepository = connection.getCustomRepository(TestRepository);
});
[...]
It seems like the testRepository
and connection
are initialized correctly by on the line this.unitOfWorkQueryRunner = this.connection.createQueryRunner()
I get the error createQueryRunner
is not a function.
What am I doing wrong?
EDIT:
connection
:
<ref *1> Connection {
migrations: [
CreateBrandTable1628717011030 {
name: 'CreateBrandTable1628717011030'
}
],
subscribers: [ GlobalSubscriber {} ],
entityMetadatas: [
EntityMetadata {
childEntityMetadatas: [],
inheritanceTree: [Array],
tableType: 'regular',
withoutRowid: false,
synchronize: true,
hasNonNullableRelations: false,
isJunction: false,
isAlwaysUsingConstructor: true,
isClosureJunction: false,
hasMultiplePrimaryKeys: false,
hasUUIDGeneratedColumns: true,
ownColumns: [Array],
columns: [Array],
ancestorColumns: [],
descendantColumns: [],
nonVirtualColumns: [Array],
ownerColumns: [],
inverseColumns: [],
generatedColumns: [Array],
primaryColumns: [Array],
ownRelations: [],
relations: [],
eagerRelations: [],
lazyRelations: [],
oneToOneRelations: [],
ownerOneToOneRelations: [],
oneToManyRelations: [],
manyToOneRelations: [],
manyToManyRelations: [],
ownerManyToManyRelations: [],
relationsWithJoinColumns: [],
relationIds: [],
relationCounts: [],
foreignKeys: [],
embeddeds: [],
allEmbeddeds: [],
ownIndices: [],
indices: [],
uniques: [],
ownUniques: [],
checks: [],
exclusions: [],
ownListeners: [],
listeners: [],
afterLoadListeners: [],
beforeInsertListeners: [],
afterInsertListeners: [],
beforeUpdateListeners: [],
afterUpdateListeners: [],
beforeRemoveListeners: [],
afterRemoveListeners: [],
connection: [Circular *1],
inheritancePattern: undefined,
treeType: undefined,
treeOptions: undefined,
parentClosureEntityMetadata: undefined,
tableMetadataArgs: [Object],
target: [class Brand extends CustomBaseEntity],
expression: undefined,
engine: undefined,
database: undefined,
schema: 'sh',
givenTableName: undefined,
targetName: 'Brand',
tableNameWithoutPrefix: 'brand',
tableName: 'brand',
name: 'Brand',
tablePath: 'sh.brand',
orderBy: undefined,
discriminatorValue: 'Brand',
treeParentRelation: undefined,
treeChildrenRelation: undefined,
createDateColumn: [ColumnMetadata],
updateDateColumn: undefined,
deleteDateColumn: undefined,
versionColumn: undefined,
discriminatorColumn: undefined,
treeLevelColumn: undefined,
nestedSetLeftColumn: undefined,
nestedSetRightColumn: undefined,
materializedPathColumn: undefined,
objectIdColumn: undefined,
propertiesMap: [Object]
}
],
name: 'default',
options: {
entities: [
...
],
migrations: [
...
],
subscribers: [
...
],
name: 'default',
type: 'postgres',
host: 'localhost',
port: '5432',
database: 'database_name',
schema: 'sh',
username: 'sh_user',
password: 'password'
},
logger: AdvancedConsoleLogger { options: undefined },
driver: PostgresDriver {
slaves: [],
connectedQueryRunners: [],
isReplicated: false,
treeSupport: true,
supportedDataTypes: [
'int',
'int2',
'int4',
'int8',
'smallint',
'integer',
'bigint',
'decimal',
'numeric',
'real',
'float',
'float4',
'float8',
'double precision',
'money',
'character varying',
'varchar',
'character',
'char',
'text',
'citext',
'hstore',
'bytea',
'bit',
'varbit',
'bit varying',
'timetz',
'timestamptz',
'timestamp',
'timestamp without time zone',
'timestamp with time zone',
'date',
'time',
'time without time zone',
'time with time zone',
'interval',
'bool',
'boolean',
'enum',
'point',
'line',
'lseg',
'box',
'path',
'polygon',
'circle',
'cidr',
'inet',
'macaddr',
'tsvector',
'tsquery',
'uuid',
'xml',
'json',
'jsonb',
'int4range',
'int8range',
'numrange',
'tsrange',
'tstzrange',
'daterange',
'geometry',
'geography',
'cube',
'ltree'
],
spatialTypes: [ 'geometry', 'geography' ],
withLengthColumnTypes: [
'character varying',
'varchar',
'character',
'char',
'bit',
'varbit',
'bit varying'
],
withPrecisionColumnTypes: [
'numeric',
'decimal',
'interval',
'time without time zone',
'time with time zone',
'timestamp without time zone',
'timestamp with time zone'
],
withScaleColumnTypes: [ 'numeric', 'decimal' ],
mappedDataTypes: {
createDate: 'timestamp',
createDateDefault: 'now()',
updateDate: 'timestamp',
updateDateDefault: 'now()',
deleteDate: 'timestamp',
deleteDateNullable: true,
version: 'int4',
treeLevel: 'int4',
migrationId: 'int4',
migrationName: 'varchar',
migrationTimestamp: 'int8',
cacheId: 'int4',
cacheIdentifier: 'varchar',
cacheTime: 'int8',
cacheDuration: 'int4',
cacheQuery: 'text',
cacheResult: 'text',
metadataType: 'varchar',
metadataDatabase: 'varchar',
metadataSchema: 'varchar',
metadataTable: 'varchar',
metadataName: 'varchar',
metadataValue: 'text'
},
dataTypeDefaults: {
character: [Object],
bit: [Object],
interval: [Object],
'time without time zone': [Object],
'time with time zone': [Object],
'timestamp without time zone': [Object],
'timestamp with time zone': [Object]
},
maxAliasLength: 63,
connection: [Circular *1],
options: {
entities: [Array],
migrations: [Array],
subscribers: [Array],
name: 'default',
type: 'postgres',
host: 'localhost',
port: '5432',
database: 'database_name',
schema: 'sh',
username: 'sh_user',
password: 'password'
},
postgres: PG {
defaults: [Object],
Client: [Function],
Query: [class Query extends EventEmitter],
Pool: [class BoundPool extends Pool],
_pools: [],
Connection: [class Connection extends EventEmitter],
types: [Object],
DatabaseError: [class DatabaseError extends Error]
},
database: 'competitor_monitoring_tool_test',
master: BoundPool {
_events: [Object: null prototype],
_eventsCount: 1,
_maxListeners: undefined,
options: [Object],
log: [Function (anonymous)],
Client: [Function],
Promise: [Function: Promise],
_clients: [Array],
_idle: [Array],
_pendingQueue: [],
_endCallback: undefined,
ending: false,
ended: false,
[Symbol(kCapture)]: false
}
},
manager: EntityManager {
repositories: [],
plainObjectToEntityTransformer: PlainObjectToNewEntityTransformer {},
connection: [Circular *1]
},
namingStrategy: DefaultNamingStrategy {
nestedSetColumnNames: { left: 'nsleft', right: 'nsright' },
materializedPathColumnName: 'mpath'
},
queryResultCache: undefined,
relationLoader: RelationLoader { connection: [Circular *1] },
isConnected: true
}
Repo to reproduce a similar issue (probably it's the same config issue): https://github.com/y-chen/nestjs-typeorm-undefined-issue