Reputation: 121
Well I see a very similar problem here, sadly there was no definitive answer: TypeORM & NestJS: TypeError: Cannot read properties of undefined (reading 'joinColumns')
I have an existing DB and I wanted to try making this app as a test to interact with it. I went with NestJS, and the docs mention TypeORM being the tightest integration so I'm trying that. I can query the DB just fine with entities, but trying to include an association is failing. I'm stuck on pretty much the first hurdle, alas. I'll switch if I have to, but I don't want to just give up on fixing this. There must be one little thing missing or incorrect I'm sure.
The exact error:
TypeError: Cannot read properties of undefined (reading 'joinColumns')
at <anonymous> (C:\...REDACTED!\node_modules\typeorm\query-builder\src\query-builder\SelectQueryBuilder.ts:2319:39)
at Array.map (<anonymous>)
at SelectQueryBuilder.createJoinExpression (C:\...REDACTED!\node_modules\typeorm\query-builder\src\query-builder\SelectQueryBuilder.ts:2257:57)
at SelectQueryBuilder.getQuery (C:\...REDACTED!\node_modules\typeorm\query-builder\src\query-builder\SelectQueryBuilder.ts:87:21)
at SelectQueryBuilder.executeEntitiesAndRawResults (C:\...REDACTED!\node_modules\typeorm\query-builder\src\query-builder\SelectQueryBuilder.ts:3477:26)
at SelectQueryBuilder.getRawAndEntities (C:\...REDACTED!\node_modules\typeorm\query-builder\src\query-builder\SelectQueryBuilder.ts:1670:40)
at SelectQueryBuilder.getOne (C:\...REDACTED!\node_modules\typeorm\query-builder\src\query-builder\SelectQueryBuilder.ts:1697:36)
at EntityManager.findOne (C:\...REDACTED!\node_modules\typeorm\entity-manager\src\entity-manager\EntityManager.ts:1215:14)
at Repository.findOne (C:\...REDACTED!\node_modules\typeorm\repository\src\repository\Repository.ts:597:29)
at AppController.getClients (C:\...REDACTED!\src\app.controller.ts:24:46)
It seems the inverse relation isn't loading (last code block in the code detail section).
Here are my two classes with unrelated properties stripped out:
@Entity({ name: 'Agent', schema: 'dbo' })
export class EntityAgent {
@PrimaryGeneratedColumn({ name: 'Id' })
id: number
@Column({ name: 'ClientId' })
id_client: number
...
@ManyToOne(() => EntityClient, (relation) => relation.id)
client: Relation<EntityClient>
}
@Entity({ name: 'Client', schema: 'dbo' })
export class EntityClient {
@PrimaryGeneratedColumn({ name: 'Id' })
id: number
...
@OneToMany(() => EntityAgent, (relation) => relation.id_client)
agents: Relation<EntityAgent[]>
}
I'm attempting to get the clients from the DB like this:
// tried with repository pattern
await this.repositoryClient.findOne({
where: { id: 4 },
relations: { agents: true },
})
// and using the datasource manager
await this.dataSource.manager.findOne(EntityClient, {
where: { id: 4 },
relations: { agents: true },
})
And here's how I'm initializing the app
@Module({
imports: [
...
TypeOrmModule.forRootAsync({
// name: 'agent',
imports: [ConfigModule],
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
type: 'mssql',
host: config.get('database.host', { infer: true }),
port: config.get('database.port', { infer: true }),
database: config.get('database.db_agent_name', { infer: true }),
username: config.get('database.db_agent_user', { infer: true }),
password: config.get('database.db_agent_pass', { infer: true }),
logging: config.get('server.environment', { infer: true }) === 'local',
entities: [
EntityAgent, EntityClient,
],
options: {
encrypt: true,
trustServerCertificate: config.get('server.environment', { infer: true }) === 'local',
},
}),
}),
TypeOrmModule.forFeature([
EntityClient,
]),
AdminModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
And with debugger, I've determined it's failing at this code in the typeorm library
createJoinExpression() {
const joins = this.expressionMap.joinAttributes.map((joinAttr) => {
const relation = joinAttr.relation;
const destinationTableName = joinAttr.tablePath;
const destinationTableAlias = joinAttr.alias.name;
...
else if (relation.isOneToMany || relation.isOneToOneNotOwner) {
// JOIN `post` `post` ON `post`.`categoryId` = `category`.`id`
const condition = relation.inverseRelation.joinColumns.map((joinColumn) => { // <-- RIGHT HERE, relation.inverseRelation is undefined
...
Annoyingly, I don't really know where to troubleshoot from here. Here's a sample of what I've tried:
dist
and node_modules
and reinstall/rebuild everything.{ agents: true }
to confirm the entities work by themselves. I do pull data successfully when I do not try to load the association.ManyToOne()
and all relation stuff on the Agent side. No change.Relation<>
to wrap the property. No change.findOne()
query (which seems to be the older TypeORM way). No change.@JoinColumn({ name: 'id', referencedColumnName: 'id_client' })
on the Client entity agents prop along with the @OneToMany()
. No change.@OneToMany(() => EntityAgent, (relation) => relation.id_client)
to something else (like client
). Hey, error in the DB because the column doesn't exist. That I'd expect though.lazy
and eager
in the client's OneToMany()
decorator options, I tried true/false for both options. No change.Upvotes: 0
Views: 201
Reputation: 121
Not the most satisfying answer, but my solution was to stop trying to use TypeORM. Apparently plenty of people have had issues with this.
I used Sequelize instead, worked instantly.
I had fun digging into TypeORM, I was following the initialization of everything line by line with the debugger, and there's a part where it compares the property paths of the models on both sides. It would not respect the relation column name when matching the model properties unless it was the associated property in the model (EntityAgent.client or EntityClient.agents), but that would just make it fail to query the DB because it would use that name to try querying the DB. I dunno, but I had to stop trying so I could actually get stuff done.
Upvotes: -1