Initialize Forest Admin in creating project

Feature(s) impacted

hello
I’m trying to follow the guide creating new project
i select Advanced setup → inside my own app → selected Node js, express, Sequelize. → install Forest Package → Initialize Forest Admin → Configure my environment variables → npm start
it successfully mounted on my Express.js
but i think it has some problem with adding data source.
my Express and Sequelize version satisfy requirements.
Node : 16.13.0
Sequelize : ^6.26.0
Express : ^4.18.2
@forestadmin/agent : ^1.9.2
@forestadmin/datasource-sequelize : ^1.3.1

Observed behavior

“Cannot convert undefined or null to object” type error message came out from node_modules/@forestadmin/datasource-sequelize/dist/datasource.js:24:16

Expected behavior

complete my setup successfully

Failure Logs

TypeError: Cannot convert undefined or null to object
at Function.values ()
at SequelizeDataSource.createCollections (/server/node_modules/@forestadmin/datasource-sequelize/dist/datasource.js:24:16)

Context

  • Agent (forest package) name & version: @forestadmin/agent(^1.9.2) @forestadmin/datasource-sequelize(^1.3.1)
  • Database type: MySql

Hello @Ian and welcome to our community !

I am sorry to hear that you encounter issues with the onboarding.

Is it possible that the database you have used for the onboarding is empty ? Forest Admin requires a database with at least one collection defined.

Thank you for your reply
My database is not empty. i defined some tables and there’s no problem with CRUD data.
Could using Amazon RDS be a problem?

Not at all, it is a very common setup among our customers.

As per the source code the line that is throwing an error is when we try to access sequelize.models which seems to be null.

Can you:

  • Check that you loaded your models into sequelize because initializing our agent (we need the models definitions for startup)
  • Check that you are passing the proper arguments in the createSequelizeDataSource call
1 Like

Thank you for the solution. and sorry for the late reply.
I was trying to fix my code after seeing the link you posted.
I’m embarrassed to say this, but I haven’t solved the problem yet.

and this is my code…

server
|_models
| |
| |_index.js
| |_user.js
|
|_index.js

index.js

require("dotenv").config();
const express = require("express");
const cookieParser = require("cookie-parser");
const cors = require("cors");
const app = express();
const indexRouter = require("./routes");
const port = process.env.SERVER_PORT || 80;

const { createAgent } = require('@forestadmin/agent');
const { createSequelizeDataSource } = require('@forestadmin/datasource-sequelize');
const sequelizeInstance = require('./models');

createAgent({
    authSecret: process.env.FOREST_AUTH_SECRET,
    envSecret: process.env.FOREST_ENV_SECRET,
    isProduction: process.env.NODE_ENV === 'production',
})
.addDataSource(createSequelizeDataSource(sequelizeInstance))
.mountOnExpress(app)
.start()

app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cookieParser());
app.use(
    cors({
        origin: ["..."],
        method: ["GET", "POST", "PATCH", "PUT", "DELETE"],
        credentials: true,
        SameSite: "None",
    }),
);
app.use("/", indexRouter);
app.listen(port, () => {
    console.log(`Server is running on port ${port}`);
});

module.exports = app;

server/models/index.js

'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const process = require('process');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.js');
const db = {};

let sequelize = new Sequelize({
  host: config.host,
  username: config.username,
  password: config.password,
  port: config.port,
  database: config.database,
  dialect: 'mysql'
})

fs
  .readdirSync(__dirname)
  .filter(file => {
    return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
  })
  .forEach(file => {
    const model = require(path.join(__dirname, file))(sequelize, Sequelize.DataTypes);
    db[model.name] = model;
  });

Object.keys(db).forEach(modelName => {
  if (db[modelName].associate) {
    db[modelName].associate(db);
  }
});

db.sequelize = sequelize;
db.Sequelize = Sequelize;

module.exports = db;

server/models/user.js

require("dotenv").config();
const { createAgent } = require('@forestadmin/agent');
const { createSequelizeDataSource } = require('@forestadmin/datasource-sequelize');
"use strict"

const { Model } = require('sequelize');
module.exports = (sequelize, DataTypes) => { 
  class User extends Model {
    static associate(models) {
      this.hasOne(models.Purchase, { foreignKey: "userId", sourceKey: "id" });
      this.hasOne(models.UserInfo, { foreignKey: "userId", sourceKey: "id" });
    }
  }
  User.init(
    {
      id: {
        type: DataTypes.INTEGER,
        autoIncrement: true,
        primaryKey: true,
      },
      identification: {
        type: DataTypes.STRING,
        allowNull: false,
      },  
      password: {
        type: DataTypes.STRING,
        allowNull: false,
      },
      email: {
        type: DataTypes.STRING,
        allowNull: false,
      },
    }, 
    { sequelize, modelName: 'User',}
  );
  // Create agent and import collections from sequelize
  const agent = createAgent({
    authSecret: process.env.FOREST_AUTH_SECRET,
    envSecret: process.env.FOREST_ENV_SECRET,
    isProduction: process.env.NODE_ENV === 'production',
  }).addDataSource(createSequelizeDataSource(sequelize));
  return User;
};

I tried to fix the model file after seeing link you sent me, but I don’t understand it well.
should I make agent in model file and main application file both?
and does the agent in the model file just have to declare?

I would appreciate it if you could give me a solution to the insufficient questions. Or is there any example file to refer to?

I think the documentation mislead you, you only need to instantiate the agent once in your index.js file.

As you can see in the server/models/index.js file, we create a Sequelize instance that will be used to define every model present in the folder, the instance will then be exported via the object db. You simply need to require it in your index.js file and pass it to createSequelizeDataSource.

It should be something like createSequelizeDataSource(db.sequelize)

1 Like

I solved the problem with your advice. Thank you very much! :slight_smile:

1 Like