sequelize-typescript
sequelize-typescript copied to clipboard
How are people dealing with circular dependencies?
I have models define in separate files that reference each other. Is there a way to deal with circular dep issues without combining the models into one file?
import { Table, Column, Model, HasOne } from 'sequelize-typescript';
import User from './User.model';
@Table({ timestamps: true })
export default class Post extends Model {
@HasOne(() => User)
author!: User;
@Column
title!: string;
@Column
content!: string;
}
import { Table, Column, Model, HasMany } from 'sequelize-typescript';
import Post from './Post.model';
@Table({ timestamps: true })
export default class User extends Model {
@Column
name!: string;
@HasMany(() => Post)
posts!: Post[];
}
I am also fighting with this, and my "solution" up until now has been to disable the linter rule, and so far, everything has been happy. I'm guessing, though, that I've just been lucky and it'll bite me sooner or later. I would also love to know what people are doing to solve this problem, as I certainly don't want to put all my models into a single gigantic file.
There's really no way around it, that's why they're using arrow functions to defer the model resolution in associations. As long as you don't start importing other unnecessary stuff to your entity files, you should be alright.
Adding to this thread as I was facing the same problem. Is just ignoring the linter error the best way of dealing with this?
There's really no way around it, that's why they're using arrow functions to defer the model resolution in associations. As long as you don't start importing other unnecessary stuff to your entity files, you should be alright.
No, it's not alright. It prevents models from being imported when using dynamic import. We were somewhat alright using webpack 4 require (I'm assuming out reload problems were still caused by it), but in webpack 5, it actually doesn't work anymore.
The correct approach would be for the e.g. @BelongsTo association to receive the models objects with statically initialized models in order to do post initialization association.
IMHO the code below should be adjusted to help avoid the need for recursive imports:
private associateModels(models: ModelCtor[]): void {
models.forEach((model) => {
const associations = getAssociations(model.prototype);
if (!associations) return;
associations.forEach((association) => {
const options = association.getSequelizeOptions(model, this);
// const associatedClass = this.model(association.getAssociatedClass());
// should become
const associatedClass = this.model(association.getAssociatedClass(models));
if (!associatedClass.isInitialized) {
throw new ModelNotInitializedError(
associatedClass,
`Association between ${associatedClass.name} and ${model.name} cannot be resolved.`
);
}
model[association.getAssociation() as any](associatedClass, options as any);
});
});
}
So that
import Company from './Company';
[...]
@BelongsTo(() => Company, {
foreignKey: 'companyId',
as: 'company',
})
public company!: Company;
becomes
@BelongsTo((models) => models.Company, {
foreignKey: 'companyId',
as: 'company',
})
public company!: Company;
@RobinBuschmann I took an initial stab at this. Would be great if someone could give me some feedback on whether this is a good idea or not: https://github.com/mschipperheyn/sequelize-typescript/commit/89bbef67201394d2fe4d832a8e2e8c4565e67bb7
I've added a PR though. It does solve circular depencencies as being thrown by the webpack CircularDependencyPlugin https://github.com/RobinBuschmann/sequelize-typescript/pull/1206