Enrich your models

⚠️ This page is relevant only if you installed Forest Admin directly on a database (SQL/Mongodb). If you installed in a Rails/Django/Laravel app, you manage your models like you normally would.

Please be sure of your agent type and version and pick the right documentation accordingly.

This is the documentation of the forest-express-sequelize and forest-express-mongoose Node.js agents that will soon reach end-of-support.

forest-express-sequelize v9 and forest-express-mongoose v9 are replaced by @forestadmin/agent v1.

Please check your agent type and version and read on or switch to the right documentation.

Enrich your models

Declaring a new model

Whenever you have a new table/collection in your database, you will have to create file to declare it. Here is a template example for a companies table:

/models/companies.js
module.exports = (sequelize, DataTypes) => {
  const { Sequelize } = sequelize;
  const Company = sequelize.define('companies', {
    name: {
      type: DataTypes.STRING,
    },
    createdAt: {
      type: DataTypes.DATE,
    },
    ...
  }, {
    tableName: 'companies',
    underscored: true,
    schema: process.env.DATABASE_SCHEMA,
  });

  Company.associate = (models) => {
  };

  return Company;
};

Fields within that model should match your table's fields as shown in next section.

New relationships may be added there:

Company.associate = (models) => {};

You can learn more about relationships on this dedicated page.

When you manually add a new model, you need to configure the permissions for the corresponding collection in the UI (allow record details view, record creation, record edit, etc). By default a new collection is not visible and all permissions are disabled. You can set permissions by going to the Roles settings.

Declaring a new field in a model

Any new field must be added manually within the corresponding model of your /models folder.

Fields are declared as follows:

createdAt: {
  type: DataTypes.DATE,
},

An exhaustive list of DataTypes can be found in Sequelize documentation.

You can see how that snippet fits into your code in the model example above.

Managing nested documents in Mongoose

For a better user experience, you can Flatten nested fields.

Lumber introspects your data structure recursively, so nested fields (object in object) are detected any level deep. Your sub-documents (array of nested fields) are detected as well.

Conflicting data types will result in the generation of a mixed type field.

The following model...

module.exports = (mongoose, Mongoose) => {
  const schema = Mongoose.Schema({
    // Level 0
    'age': Number,
    'id': Number,
    'name': String,
    // Level 1
    'address':{
      'addressDetail': String,
      'area': String,
      'city': String,
      'pincode': Number,
    },
    // Level 2
    'contactDetails':{
      'phone':{
        'homePhone': String,
        'mobilePhone': String,
      },
      'email': String,
    },
    // Related data
    'booksRead':[{
      'name': String,
      'authorName': String,
      'publishedBy': String,
    }],
  }, {
    timestamps: false,
  });

  return mongoose.model('testCollection', schema, 'testCollection');
};

...will result in the following interface:

Removing a model

By default all tables/collections in your database are analyzed by Lumber to generate your models. If you want to exclude some of them to prevent them from appearing in your Forest, check out this how-to.

Adding validation to your models

Validation allows you to keep control over your data's quality and integrity.

If your existing app already has validation conditions, you may - or may not - want to reproduce the same validation conditions in your admin backend's models.

If so, you'll have to do it manually, using the below examples.

Depending on your database type, your models will have been generated in Sequelize (for SQL databases) or Mongoose (for Mongo databases).

In Sequelize, you add validation using the validate property:

/models/customers.js
module.exports = (sequelize, DataTypes) => {
  const Customer = sequelize.define('customers', {
    ...
    'email': {
      type: DataTypes.STRING,
      validate: {
        isEmail: true,
        len: [10,25]
      }
    },
    ...
  },
  ...
  return Customer;
};

The 2 validators above will have the following effect on your email field:

For an exhaustive list of available validators, check out the Sequelize documentation.

Adding a default value to your models

You can choose to add a default value for some fields in your models. As a result, the corresponding fields will be prefilled with their default value in the creation form:

/models/customers.js
module.exports = (sequelize, DataTypes) => {
  const Customer = sequelize.define('customers', {
    ...
    'firstname': {
      'type': DataTypes.STRING,
      'defaultValue': 'Marc'
    },
    ...
  },
  ...
  return Customer;
};

Adding a hook

Hooks are a powerful mechanism which allow you to automatically trigger an event at specific moments in your records lifecycle.

In our case, let's pretend we want to update a update_count field every time a record is updated:

To add a beforeSave hook in Sequelize, use the following syntax:

/models/orders.js
module.exports = (sequelize, DataTypes) => {
  var Order = sequelize.define('orders', {
    ...
    'update_count': {
      'type': DataTypes.INTEGER,
      'defaultValue': 0
    },
    ...
  },
  ...
  Order.beforeSave((order, options) => {
      order.update_count += 1;
    }
  );

  return Order;
};

Every time the order is updated, the updateCount field will be incremented by 1:

The exhaustive list of available hooks in Sequelize are available here.

Last updated