Relations not working after upgrading to v6 and typescript

Hi Team,

We face an issue with relations after upgrading.

We have 4 models:

  1. Agents
  2. Features
  3. Accounts
  4. AccountFeatures

==============

  1. Agents are related to the Accounts(All is working fine):
Agents.belongsTo(Accounts, {
        foreignKey: 'tymeshiftAccountId',
        as: 'Account'
    });
  1. Features are related to the Accounts(All is working fine):
    Features.belongsToMany(Accounts, {
          through: 'account_features',
          foreignKey: 'featureId',
          otherKey: 'tymeshiftCustomerId',
          as: 'Accounts'
        }
    );
  1. Accounts are related to the Features and Agents:
    Accounts.belongsToMany(Features, {
        through: 'account_features',
        foreignKey: 'tymeshiftCustomerId',
        otherKey: 'featureId',
        as: 'Features'
    });
    Accounts.hasMany(Agents, {
        foreignKey: 'tymeshiftAccountId',
        as: 'Agents'
    });

We could see this issue and relation is not presented on the UI Collection page for the Accounts:

Model creation error: TypeError: Cannot read property 'toLowerCase' of undefined
  1. AccountsFeature relations:
    AccountFeatures.belongsTo(Features, {
        foreignKey: {
            name: 'featureId',
            field: 'id',
        },
        as: 'Feature',
    });
    AccountFeatures.belongsTo(Accounts, {
        foreignKey: {
            name: 'tymeshiftAccountId',
            field: 'id',
        },
        as: 'Account',
    });

Error:

Model creation error: TypeError: Cannot read property 'undefined' of undefined

We tried with the camel case and snake case on all relations, and the results are the same.

Any suggestions?

Hey @Bojan_Antonijevic,

Could you try to remove the relation one by one and tell us which one does not work ?
Do you have all the stacktrace of the error ?

Hey @vince,

We just have an error in the terminal without stacktrace.

Yea we are tried to remove one by one and we have the same errors on Accounts and AccountFeature models.

Both collections appeared well on the UI so I guess the models and interfaces are written well. We are also tried to use only 2 fields per collection which we need for relations. The same thing happens.

When we enable stack trace on the models/index.js

console.log(file);
console.log(error.stack);
console.error('Model creation error: ' + error);

and when we:

Accounts.belongsToMany(Features, {
        through: 'account_features',
        foreignKey: 'tymeshiftCustomerId',
        otherKey: 'featureId',
        as: 'Features' // **** REMOVED THIS ****
    });

We get this:

TypeError: Cannot read property 'name' of undefined
    at new BelongsToMany (/Users/bojanantonijevic/Desktop/web-app/forest-admin/node_modules/sequelize/lib/associations/belongs-to-many.js:97:37)
    at Function.belongsToMany (/Users/bojanantonijevic/Desktop/web-app/forest-admin/node_modules/sequelize/lib/associations/mixin.js:64:25)
    at exports.default (/Users/bojanantonijevic/Desktop/web-app/forest-admin/dist/models/accounts.js:538:24)
    at Sequelize.import (/Users/bojanantonijevic/Desktop/web-app/forest-admin/node_modules/sequelize/lib/sequelize.js:482:38)
    at /Users/bojanantonijevic/Desktop/web-app/forest-admin/dist/models/index.js:26:40
    at Array.forEach (<anonymous>)
    at Object.<anonymous> (/Users/bojanantonijevic/Desktop/web-app/forest-admin/dist/models/index.js:24:6)
    at Module._compile (internal/modules/cjs/loader.js:1158:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
    at Module.load (internal/modules/cjs/loader.js:1002:32)
    at Function.Module._load (internal/modules/cjs/loader.js:901:14)
    at Module.require (internal/modules/cjs/loader.js:1044:19)
    at require (internal/modules/cjs/helpers.js:77:18)
    at Object.<anonymous> (/Users/bojanantonijevic/Desktop/web-app/forest-admin/dist/middlewares/forestadmin.js:5:23)
    at Module._compile (internal/modules/cjs/loader.js:1158:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
    at Module.load (internal/modules/cjs/loader.js:1002:32)
    at Function.Module._load (internal/modules/cjs/loader.js:901:14)
    at Module.require (internal/modules/cjs/loader.js:1044:19)
    at require (internal/modules/cjs/helpers.js:77:18)
    at /Users/bojanantonijevic/Desktop/web-app/forest-admin/node_modules/require-all/index.js:56:46
    at Array.forEach (<anonymous>)
    at requireAll (/Users/bojanantonijevic/Desktop/web-app/forest-admin/node_modules/require-all/index.js:34:9)
    at Object.<anonymous> (/Users/bojanantonijevic/Desktop/web-app/forest-admin/dist/app.js:39:1)
    at Module._compile (internal/modules/cjs/loader.js:1158:30)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1178:10)
    at Module.load (internal/modules/cjs/loader.js:1002:32)
    at Function.Module._load (internal/modules/cjs/loader.js:901:14)
    at Function.executeUserEntryPoint [as runMain] (internal/modules/run_main.js:74:12)
    at internal/main/run_main_module.js:18:47

By this we could track the error to this file:
dist/models/accounts.js and line

accounts_1.default.belongsToMany(features_1.default, { // **** THIS LINE ****
        through: 'account_features',
        foreignKey: 'tymeshiftCustomerId',
        otherKey: 'featureId'
});

I guess it could not recognize the feature’s interface. We implement it as:

import Features from '../interfaces/features';

and this is compiled as:

const features_1 = require("../interfaces/features");

Comparing with other examples all look fine, but still…

Hi @Bojan_Antonijevic,

I have currently no clue on what causes your issue. could I take a look at the interfaces/features file’s content?

Hi @anon37102731,

Sure, I just simplify it a bit for security reasons and easier reading. Also changed the name of the tables in the post for security risk also.

We test it on a simple example with reading table name from the model as:
Model.getTableName()

In some places is working on some is not. It looks like some interfaces are not loaded in some models, but on some others are loaded correctly. Also, we created some test models and interfaces. Behaviour is the same, we could not load it on every model.

models/features.ts - all is working fine

import Features from '../interfaces/features';
import Accounts from '../interfaces/accounts';
import Agents from '../interfaces/agents';
import Test from '../interfaces/test';

export default (sequelize, DataTypes) => {
    Features.init(
        {
            id: {
                type: DataTypes.STRING,
                primaryKey: true,
            },
            name: {
                type: DataTypes.STRING,
            },
            slug: {
                type: DataTypes.STRING,
            },
            status: {
                type: DataTypes.STRING,
            },
            createdAt: {
                type: DataTypes.DATE,
            },
            updatedAt: {
                type: DataTypes.DATE,
            },
            deletedAt: {
                type: DataTypes.DATE,
            }
        }, {
            tableName: 'TABLE_NAME',
            modelName: 'features',
            underscored: true,
            sequelize
        }
    );

    console.log('================FEATURES================');
    // All console below is working and we can read table names from all other collections
    console.log(Features.getTableName());
    console.log(Accounts.getTableName());
    console.log(Agents.getTableName());
    console.log(Test.getTableName());
    console.log('================/FEATURES================');

    Features.belongsToMany(Accounts, {
          through: 'ACCOUNT_FEATURES_RELATION_TABLE_NAME',
          foreignKey: 'featureId',
          otherKey: 'tymeshiftCustomerId',
          as: 'Accounts'
        }
    );

    return Features;
}

interfaces/features.ts

import { Model } from "sequelize";

export default class Features extends Model {
    public id!: string;
    public name!: string;
    public slug!: string;
    public status!: string;
    public readonly createdAt: Date;
    public readonly updatedAt: Date;
    public readonly deletedAt: Date;
}

================================
models/accounts.ts

import Accounts from '../interfaces/accounts';
import Features from '../interfaces/features';
import Test from '../interfaces/test';
import Agents from '../interfaces/agents';

export default (sequelize, DataTypes) => {
    Accounts.init(
        {
            id: {
                type: DataTypes.BIGINT,
                primaryKey: true,
            },
            fname: {
                type: DataTypes.STRING,
            },
            lname: {
                type: DataTypes.STRING,
            },
            email: {
                type: DataTypes.STRING,
            },
            companyName: {
                type: DataTypes.STRING,
                field: 'company_name',
            }
        }, {
            tableName: 'ACCOUNTS_TABLE_NAME',
            modelName: 'accounts',
            underscored: true,
            timestamps: false,
            sequelize
        }
    );

    console.log('================ACCOUNTS================');
    console.log(Accounts.tableName);  // Work fine
    // For every other console we get 
    // Model creation error: TypeError: Cannot read property 'getQueryInterface' of undefined
    console.log(Test.getTableName());
    console.log(Features.getTableName());
    console.log(Agents.getTableName());
    console.log('================/ACCOUNTS================');
    Accounts.belongsToMany(Features, {
        through: 'ACCOUNTS_FEATURE_RELATION_TABLE_NAME',
        foreignKey: 'tymeshiftCustomerId',
        otherKey: 'featureId',
        as: 'Features'
    });
    Accounts.hasMany(Agents, {
        foreignKey: 'tymeshiftAccountId',
        as: 'Agents'
    });

    return Accounts;
}

interfaces/accounts.ts

import { Model } from "sequelize";

export default class Accounts extends Model {
    public id!: number;
    public fname!: string;
    public lname!: string;
    public email!: string;
    public companyName!: string;
}

================================
models/agents.ts

import Agents from '../interfaces/agents';
import Accounts from '../interfaces/accounts';
import Features from "../interfaces/features";
import Test from "../interfaces/test";

export default (sequelize, DataTypes) => {
    Agents.init(
        {
            id: {
                type: DataTypes.BIGINT,
                primaryKey: true,
            },
            useremail: {
                type: DataTypes.STRING,
            },
            userpassword: {
                type: DataTypes.STRING,
            },
            username: {
                type: DataTypes.STRING,
            },
            tymeshiftAccountId: {
                type: DataTypes.BIGINT,
            },
        }, {
            tableName: 'AGENTS_TABLE_NAME',
            modelName: 'agents',
            underscored: true,
            timestamps: false,
            sequelize
        }
    );
    console.log('================AGENTS================');
    console.log(Agents.getTableName()); // Work fine
    console.log(Accounts.getTableName()); // Work fine
    console.log(Test.getTableName()); // Model creation error: TypeError: Cannot read property 'getQueryInterface' of undefined
    console.log(Features.getTableName()); // Model creation error: TypeError: Cannot read property 'getQueryInterface' of undefined
    console.log('================/AGENTS================');

    Agents.belongsTo(Accounts, {
        foreignKey: 'tymeshiftAccountId',
        as: 'Account'
    });

    return Agents;
};

interfaces/agents.ts

import { Model } from "sequelize";

export default class Agents extends Model {
    public id!: number;
    public useremail!: string;
    public userpassword!: string;
    public username!: string;
    public tymeshiftAccountId!: number;
}

Hello @Bojan_Antonijevic !

I just landed on this topic, trying to gather all the information I can!

I’m not much used to TypeScript on Sequelize, but from what I read on its documentation, you apparently need to define associations in your interfaces, as per this link : https://sequelize.org/v5/manual/typescript.html (see the first User class definition in the usage section).

I don’t know if it’s required though, could you try this out maybe ? Also, you’ll apparently need to match the association names with the as of your relationships.

Tell me if something changes with this!