NodeJS Express server using Sequelize ORM and Sequelize Auto models throws error

49 Views Asked by At

I'm on a 3 people group for a school project. The project is about a small application which communicates with the server for printing a desktop and serving JavaScript games and uses an API REST for getting and posting data. We are using Node with Express, Sequelize, and Nunjucks, but we are having issues.

Here are yjr models, generated with sequelize-automate -t js -h localhost -d clvgamehub.test -u puser -p <password> -P 5432 -e postgres -o models command.

// Ahievement.js
const Sequelize = require('sequelize');

const attributes = {
    id: {
        autoIncrement: true,
        type: DataTypes.INTEGER,
        allowNull: false,
        primaryKey: true
    },
    name: {
        type: DataTypes.STRING(20),
        allowNull: false
    },
    description: {
        type: DataTypes.STRING(90),
        allowNull: false
    },
    game_id: {
        type: DataTypes.INTEGER,
        allowNull: false,
        references: {
            model: 'Game',
            key: 'id'
        }
    }
};

const options = {
    sequelize,
    tableName: 'achievement',
    schema: 'public',
    timestamps: false,
    indexes: [
    {
        name: "achievement_pkey",
        unique: true,
        fields: [
                 { name: "id" },
                 ]
    },
    ]
};

const Achievement = sequelize.define('Achievement', attributes, options);

module.exports = Achievement;
// Game.js
const { DataTypes } = require('sequelize');
module.exports = function(sequelize) {
    const attributes = {
        id: {
            autoIncrement: true,
            type: DataTypes.INTEGER,
            allowNull: false,
            primaryKey: true
        },
        name: {
            type: DataTypes.STRING(50),
            allowNull: false
        },
        directory: {
            type: DataTypes.STRING(50),
            allowNull: false
        }
    };

    const options = {
        sequelize,
        tableName: 'game',
        schema: 'public',
        timestamps: false,
        indexes: [
        {
            name: "game_pkey",
            unique: true,
            fields: [
                     { name: "id" },
                     ]
        },
        ]
    };
    return sequelize.define('Game', attributes, options);
};
// Scoreboard.js
const { DataTypes } = require('sequelize');
module.exports = function(sequelize) {

    const attributes = {
        id: {
            autoIncrement: true,
            type: DataTypes.INTEGER,
            allowNull: false,
            primaryKey: true
        },
        user_id: {
            type: DataTypes.INTEGER,
            allowNull: false,
            references: {
                model: 'User',
                key: 'id'
            }
        },
        game_id: {
            type: DataTypes.INTEGER,
            allowNull: false,
            references: {
                model: 'Game',
                key: 'id'
            }
        },
        score: {
            type: DataTypes.INTEGER,
            allowNull: false
        }
    };

    const options = {
        sequelize,
        tableName: 'scoreboard',
        schema: 'public',
        timestamps: false,
        indexes: [
        {
            name: "scoreboard_pkey",
            unique: true,
            fields: [
                     { name: "id" },
                     ]
        },
        ]
    };

    return sequelize.define('Scoreboard', attributes, options);
};
// User.js
const { DataTypes } = require('sequelize');
module.exports = function(sequelize) {

    const attributes = {
        id: {
            autoIncrement: true,
            type: DataTypes.INTEGER,
            allowNull: false,
            primaryKey: true
        },
        nickname: {
            type: DataTypes.STRING(20),
            allowNull: false,
            unique: "user_nickname_key"
        },
        password: {
            type: DataTypes.STRING(32),
            allowNull: false
        }
    };

    const options = {
        sequelize,
        tableName: 'user',
        schema: 'public',
        timestamps: false,
        indexes: [
        {
            name: "user_nickname_key",
            unique: true,
            fields: [
                     { name: "nickname" },
                     ]
        },
        {
            name: "user_pkey",
            unique: true,
            fields: [
                     { name: "id" },
                     ]
        },
        ]
    };

    return sequelize.define('User', attributes, options);
};
// UserAchievements.js
const { DataTypes } = require('sequelize');
module.exports = function(sequelize) {

    const attributes = {
        id: {
            autoIncrement: true,
            type: DataTypes.INTEGER,
            allowNull: false,
            primaryKey: true
        },
        user_id: {
            type: DataTypes.INTEGER,
            allowNull: false,
            references: {
                model: 'User',
                key: 'id'
            }
        },
        achievement_id: {
            type: DataTypes.INTEGER,
            allowNull: false,
            references: {
                model: 'Achievement',
                key: 'id'
            }
        }
    };

    const options = {
        sequelize,
        tableName: 'user_achievements',
        schema: 'public',
        timestamps: false,
        indexes: [
        {
            name: "user_achievements_pkey",
            unique: true,
            fields: [
                     { name: "id" },
                     ]
        },
        ]
    };
    
    return sequelize.define('UserAchievements', attributes, options);
};

Here is the file for making the connection to database.

// db.js
const settings = require('./settings');
const { Sequelize } = require('sequelize');

module.exports = new Sequelize(settings.database.name, settings.database.user, settings.database.pass, {
    host: settings.database.host,
    dialect: settings.database.dialect,
    port: settings.database.port,
    logging: false,
});

The main file.

// main.js
/* import sequelize config */
const db = require('./db');

/* import sequelize models */ 
const initModels = require('./models/init-models');

/* instantiate Sequelize connector */
db.authenticate()
.then(() => {
    console.log('Connection to database successful.');
    /* initialize Sequelize models and perform relationships */
    initModels(db);
    console.log('Models instantiated.')
    

    const settings = require('./settings');
    const express = require('express');
    const cors = require('cors');
    var nunjucks = require('nunjucks');
    const bodyparser = require('body-parser');
    const server = express();

    /* import middleware */
    const RequestLogger = require('./middleware/RequestLogger');
    const ErrorLogger = require('./middleware/ErrorLogger');

    /* handlers import */
    const IndexHandler = require('./handlers/IndexHandler');
    const ApiHandler = require('./handlers/ApiHandler');




    /* template engine renderer */
    nunjucks.configure(settings.server.templates_dir, {
        autoescape: true,
        cache: false,
        express: server,
    });


    /* middleware use */
    server.use(cors());
    server.use(bodyparser.json());
    server.use(express.json());
    server.use(bodyparser.urlencoded({
        extended: true
    }));
    server.use('/static', express.static(settings.server.static_dir));

    /* request logger */
    server.use(RequestLogger);

    /* server contexts */
    server.use('/', IndexHandler);
    server.use('/api', ApiHandler);

    /* error logger */
    server.use(ErrorLogger);

    /* start the server */
    server.listen(settings.server.port, settings.server.host, () => {
        console.log("Started server on %s:%d.", settings.server.host, settings.server.port)
    });

})
.catch((e) => {
    console.log('Connection to database failed.');
    console.error(e)
});

The global settings file.

// settings.js
var settings = {
    server: {
        host: '127.0.0.1',
        port: 8080,
        base_dir: '/home/samba/share/proyecto/development/clvgamehub/',
        templates_dir: '/home/samba/share/proyecto/development/clvgamehub/templates/',
        static_dir: '/home/samba/share/proyecto/development/clvgamehub/static/',
        games_dir: '/home/samba/share/proyecto/development/clvgamehub/static/games/',
    },
    database: {
        dialect: 'postgres',
        host: '127.0.0.1',
        name: 'clvgamehub.test',
        user: 'puser',
        pass: 'secret',
        port: 5432,
    },
};

switch(process.env.NODE_ENV){
    case 'dev':
    {
        settings.server.port = 8080;
        settings.server.host = '127.0.0.1';
    }
    break;
    case 'prod':
    {
        settings.server.port = 80;
        settings.server.host = '0.0.0.0';
    }
    break;
}

module.exports = settings;

Now I have this controller I made coded:

const { Op } = require('sequelize');
const db = require('../db');

const UserAchievements = require('../models/UserAchievements')(db);
const Achievement = require('../models/Achievement')(db);
const Game = require('../models/Game')(db);

/**
 * 
 * @param achievement_id
 * @param user_id
 * @return Achievement
 * 
 **/
const getByAchievementAndUser = async(achievement_id, user_id) => {
    try {
        const achievement = await UserAchievements.findOne({
            where: {
                achievement_id: achievement_id,
                user_id: user_id,
            },
        });
        return (achievement) ? achievement : null;
    } catch (e) {
        throw e;
    }
};

/**
 * 
 * @param user_id
 * @return Achievement[]
 * 
 **/
const getByUser = async (user_id) => {
    try {
        const achievement = await UserAchievements.findAll({
            where: {
                user_id: user_id,
            },
        });
        return (achievement.length > 0)? achievement : null;
    } catch (e) {
        throw e;
    }
};

/**
 * 
 * @param game_id
 * @param user_id
 * @return Achievement[]
 * 
 **/
const getAtGameByUser = async (game_id, user_id) => {
    try {
        const achievement = await UserAchievements.findAll({
            where: {
                user_id: user_id,
            },
            include: [
            {
                model: Achievement,
                where: {
                    id: { [Op.col]: 'UserAchievements.achievement_id' },
                },
                include: [
                {
                    model: Game,
                    where: {
                        id: game_id
                    }
                }
                ]
            }
            ]
        });
        return (achievement.length > 0) ? achievement : null;
    } catch (e) {
        throw e;
    }
};

/**
 * 
 * @param achievement_id
 * @param user_id
 * @return Achievement
 * 
 **/
const unlockAchievement = async(achievement_id, user_id) => {
    try {
        const achievement = await UserAchievements.create({
            achievement_id: achievement_id,
            user_id: user_id,
        });
        return (achievement) ? achievement : null;
    } catch (e) {
        throw e;
    }
};

module.exports = {
    getByAchievementAndUser,
    getByUser,
    getAtGameByUser,
    unlockAchievement,
};

I know require functions are ok, because all the functions work fine, but the getAtGameByUser function throws this error:

EagerLoadingError [SequelizeEagerLoadingError]: Achievement is not associated to UserAchievements! at UserAchievements._getIncludedAssociation (/home/samba/share/proyecto/development/clvgamehub/node_modules/sequelize/lib/model.js:565:13) at UserAchievements._validateIncludedElement (/home/samba/share/proyecto/development/clvgamehub/node_modules/sequelize/lib/model.js:502:53) at /home/samba/share/proyecto/development/clvgamehub/node_modules/sequelize/lib/model.js:421:37 at Array.map (<anonymous>) at UserAchievements._validateIncludedElements (/home/samba/share/proyecto/development/clvgamehub/node_modules/sequelize/lib/model.js:417:39) at UserAchievements.findAll (/home/samba/share/proyecto/development/clvgamehub/node_modules/sequelize/lib/model.js:1124:12) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async Object.getAtGameByUser (/home/samba/share/proyecto/development/clvgamehub/controllers/UserAchievementsController.js:59:23)

I tried typing the associations in the init-models.js file but nothing works. It is supossed to be alright. Tables in database are well relationed. Anyways here the SQL tiny migration file:

DROP TABLE "user" CASCADE;
DROP TABLE "game" CASCADE;
DROP TABLE "achievement" CASCADE;
DROP TABLE "scoreboard" CASCADE;
DROP TABLE "user_achievements" CASCADE;

CREATE TABLE "user"(
    "id" serial,
    "nickname" varchar(20) unique not null,
    "password" varchar(32) not null,
    primary key ("id")
);

CREATE TABLE "game"(
    "id" serial,
    "name" varchar(50) not null,
    "directory" varchar(50) not null,
    primary key ("id")
);

CREATE TABLE "achievement"(
    "id" serial,
    "name" varchar(20) not null,
    "description" varchar(90) not null,
    "game_id" int not null,
    primary key ("id"),
    constraint "game_fk1" foreign key ("game_id")
        references "game"("id") on update cascade on delete cascade
);

CREATE TABLE "scoreboard"(
    "id" serial,
    "user_id" int not null, 
    "game_id" int not null,
    "score" int not null,
    primary key ("id"),
    constraint "user_fk1" foreign key ("user_id")
        references "user"("id") on update cascade on delete cascade,
    constraint "game_fk2" foreign key ("game_id")
        references "game"("id") on update cascade on delete cascade
);

CREATE TABLE "user_achievements"(
    "id" serial,
    "user_id" int not null,
    "achievement_id" int not null,
    primary key ("id"),
    constraint "user_fk2" foreign key ("user_id")
        references "user"("id") on update cascade on delete cascade,
    constraint "achievement_fk1" foreign key ("achievement_id")
        references "achievement"("id") on update cascade on delete cascade
);

I do not understand what am I doing wrong. I have looked for questions and answers here, other forums and I received help from two IA's and I cannot resolve this.

If you need me to provide more code I will. Any help is welcome! Thank you (I'm desperated).

0

There are 0 best solutions below