Application schema cannot be synchronised with Forest

Expected behavior

I am trying to set up forest admin in the dev environment for my new project (Express + Mongoose) in a similar way as I did for one of my previous project. Ideally, forest admin should connect to the app.

Actual behavior/ Failure Logs

But I am getting the following error:

[forest] 🌳🌳:deciduous_tree:  An error occured while computing the Forest schema. Your application schema cannot be synchronized with Forest. Your admin panel might not reflect your application models definition. Cannot convert undefined or null to object
{
  "stack": "TypeError: Cannot convert undefined or null to object\n    at Function.values (<anonymous>)\n    at /home/akode/Documents/WorkProjects/MConnectServer/node_modules/forest-express/dist/services/models-manager.js:25:37\n    at Array.reduce (<anonymous>)\n    at ModelsManager._flatConnectionsModels (/home/akode/Documents/WorkProjects/MConnectServer/node_modules/forest-express/dist/services/models-manager.js:24:41)\n    at ModelsManager._generateModelList 

Code Snippets:

In the server.js file, I am setting up the forest admin and importing the schema.

import express from 'express';
import cors from 'cors';
const { userSchema } = require('./models/User-Model');
const forest = require('forest-express-mongoose');
let allowedOrigins = [/\.forestadmin\.com$/, /localhost:\d{4}$/]

if (process.env.CORS_ORIGINS) {
    allowedOrigins = allowedOrigins.concat(process.env.CORS_ORIGINS.split(','));
}
const corsConfig = {
    origin: allowedOrigins,
    maxAge: 86400, // NOTICE: 1 day
    credentials: true,
};
app.use('/forest/authentication', cors({
    ...corsConfig,
    origin: corsConfig.origin.concat('null')
}));
const connectForestAdmin = async()=>{
    try {
        app.use(
            await forest.init({
                envSecret: process.env.FOREST_ENV_SECRET,
                authSecret: process.env.FOREST_AUTH_SECRET,
                objectMapping: userSchema,
                connections: { default: connection},
            })
        );
    } catch (error) {
        return error
    }
}

In the schema file I have the following code:

import { model, Schema, Model, Document } from "mongoose";
import bcrypt from "bcryptjs";
import jwt from "jsonwebtoken";

interface UserBaseDocument extends Document {
  generateToken(): string;
  tokens: any
}

interface UserInterface extends UserBaseDocument, Document {
  method: string;
  local: {
    email: string;
    password: string;
    isVerified: boolean;
    isEmailVerified: boolean;
    otp: string;
  };
  google: {
    email: string
    emailVerified: boolean
  };
  localMobile: {
    phone: string;
    countryCode: string,
    otpVerified: boolean;
    otp: string;
  };
  tokens: any,
  agreeToPolicy: boolean,
  createdAt: number;
  updatedAt: number;
}

const userSchema = new Schema(
  {
    method: {
      type: String,
      enum: ["local", "localMobile", "google"],
      required: true,
    },
    role: {
      type: String,
      enum: ["professional", "institution"],
      required: true,
    },
    agreeToPolicy: {
      type: Boolean,
      required: true
    },
    local: {
      email: {
        type: String,
      },
      password: {
        type: String,
      },
      isVerified: {
        type: Boolean,
        default: false,
      },
      isEmailVerified: {
        type: Boolean,
        default: false,
      },
      otp: {
        type: String,
      },
    },
    localMobile: {
      phone: {
        type: String,
      },
      countryCode: {
        type: String
      },
      otp: {
        type: String,
        default: null,
      },
      otpVerified: {
        type: Boolean,
        default: false,
      },
    },
    google: {
      email: {
        type: String,
      },
      emailVerified: {
        type: Boolean,
      }
    },
    tokens: [{ token: { type: String, required: true } }],
    createdAt: { type: Number },
    updatedAt: { type: Number },
  },
  { writeConcern: { w: "majority", wtimeout: 5000 } }
);

userSchema.pre<UserInterface>("validate", function (next) {
  const user = this;
  if (
    this.isNew &&
    this.method === "local" &&
    !this.isModified(this.local.password)
  ) {
    this.createdAt = this.updatedAt = Date.now();
    const salt = bcrypt.genSaltSync(Number(process.env.SALT_ROUND));
    const hash = bcrypt.hashSync(user.local.password, salt);
    // generateToken
    user.local.password = hash;
  }
  next();
});

userSchema.methods.generateToken = function (this: UserInterface) {
  const user = this;
  const payload = {
    userId: this._id,
  };
  const jwtToken = jwt.sign(payload, process.env.JWT_SIG, { expiresIn: "5d" });
  user.tokens.push({ token: jwtToken });
  user.save().then(() => {
    return jwtToken;
  });
  return jwtToken;
};

const Users: Model<any> = model("users", userSchema);

export { Users, userSchema };

Could you please help me out with what’s wrong with the configuration.

Hey @Anupam_Rana :wave:

Could you please share here the definition of the connection object given to forest.init({...}) ?
Also, from what I remember, you should have
objectMapping: require('mongoose'), instead of your current line.

Thanks in advance :pray:

1 Like

Hi @jeffladiray, Please find the definition of the connection object below:-

import mongoose from 'mongoose';
(mongoose as any).Promise = global.Promise
import dotenv from 'dotenv'
dotenv.config()



const useConnectionString = process.env.NODE_ENV === 'production' ? process.env.MASTER_DB : process.env.DEV_DB

mongoose.set('useCreateIndex', true)
const createMongoConnection = async () => {
	try {
		await mongoose.connect(useConnectionString, { useNewUrlParser: true, useUnifiedTopology: true })
		console.log('MConnect Dev DB IS Connected')
	} catch (error) {
		console.log(error)
		console.log('MConnect Dev DB Not Connected')
	}
}


export default createMongoConnection()

Initially I had objectMapping: require(‘mongoose’), but i was getting the same error.

Hey @Anupam_Rana,

objectMapping: require('mongoose') is definitely the correct value.
However, connections should contains your list of models.

On forest-cli project generation, we generate this file:

models/index.js

const fs = require('fs');
const path = require('path');
const Mongoose = require('mongoose');

const databasesConfiguration = require('../config/databases');

const connections = {};
const db = {};

databasesConfiguration.forEach((databaseInfo) => {
  const connection = Mongoose.createConnection(databaseInfo.connection.url, databaseInfo.connection.options);
  connections[databaseInfo.name] = connection;

  const modelsDir = databaseInfo.modelsDir || path.join(__dirname, databaseInfo.name);
  fs
    .readdirSync(modelsDir)
    .filter((file) => file.indexOf('.') !== 0 && file !== 'index.js')
    .forEach((file) => {
      try {
        const model = require(path.join(modelsDir, file))(connection, Mongoose);
        db[model.modelName] = model;
      } catch (error) {
        console.error(`Model creation error: ${error}`);
      }
    });
});

db.objectMapping = Mongoose;
db.connections = connections;

module.exports = db;

As you can see, connections should:

  • contains your mongoose connection (connections: { default: yourMongooseConnection }
  • All the models of your connection

Looking at the stacktrace you seems to have, I would say your connection is missing the associated models.

Let me know if it helps

1 Like

Thanks @jeffladiray it worked with a few changes, but now we are getting two more issues after when we run our application.
refer to the first screenshot

It says unable to authenticate, we also tried with forest-cli login and with a proper credential in hand but we are getting nothing on our plate.

The second issue that we’re facing is when we run our application on port :8000
The first time it runs perfectly but when we do some changes on the application we are getting message 8000 is already running. we are using nodemon on the server to watch and run our application

Thank you.

Hello @Anupam_Rana,

Regarding the login problem, can you please open the network tab in your development tools and spot the request that is failing? I would like the details about this request (headers, body, http code).

The second issue you’re mentionning is happening because you probably still have a process that has not correctly been killed by nodemon. Try to identify which process is listening and kill it manually.

Could you also post all this info in a new thread, as the first problem has been resolved.

This will help future viewers to spot the solution of your first problem, and separate the topics.

1 Like