I am unable to add models to my admin

Feature(s) impacted

Adding models to admin

Observed behavior

I do not know how to add my models so they can appear on my admin dashboard

Expected behavior

The models should appear on the dashboard

Context

const app = express()
import User from './models/user.js'
import mongoose from 'mongoose'
import mongooseModels from './forestAdmin/mongooseModels.js'

// Create your Forest Admin agent
// This must be called BEFORE all other middleware on the app
createAgent({
  authSecret: process.env.FOREST_AUTH_SECRET,
  envSecret: process.env.FOREST_ENV_SECRET,
  isProduction: process.env.NODE_ENV === 'production',

})
  // Create your Mongoose datasource
  .addDataSource(createMongooseDataSource(mongoose.createConnection(process.env.MONGODB_URL, User)))
  // Replace "myExpressApp" by your Express application
  .mountOnExpress(app)
  .start();
  • Project name: …
  • Team name: …
  • Environment name: Nodejs
  • Agent (forest package) name & version: “@forestadmin/agent”: “^1.35.0”, “@forestadmin/datasource-mongoose”: “^1.5.25”,
  • Database type: Mongodb
  • Recent changes made on your end if any: …

Hello @eakenbor! :wave: Welcome to our community :tada: :confetti_ball:

I see that you tried to use mongoose, am I right?

In this case you can create a model like this. (official mongoose doc)

const mongoose = require('mongoose');

const connectionString = 'mongodb://root:password@localhost:27017';
const connection = mongoose.createConnection(connectionString);

connection.model(
  'persons',
  new mongoose.Schema({
    name: String,
    age: Number,

    // Nested object
    address: { streetName: String, city: String, country: String },

    // List of nested objects
    bills: [{ title: String, amount: Number, issueDate: Date, payedBy: [String] }],
  }),
);

module.exports = connection;

Let me know if it helps.

Kind regards,
Morgan

Hello @morganperre, thanks for your response.
I was able to add my User model the way you suggested but I have the following issues:

  1. I am unable to add another model along side the User model.
  2. I am unable to have all the properties in my User model the default. Instead I have to use new mongoose.Schema and then create the properties I want. Is that the only way?
  3. I added a Blog Post model and I got an error that says “The request to your server either failed or returned invalid data.”. Please see the attached screenshot. My Blog Post model looks like this:
import mongoose from 'mongoose'
import slug from 'mongoose-slug-generator';
import sanitizeHtml from 'sanitize-html';
import getSignedS3Url from '../utils/getSignedS3Url.js'
import { BLOG_CATEGORIES, BLOG_STATUSES } from '../utils/enums.js';

mongoose.plugin(slug);

const CoverImage = new mongoose.Schema({
    key: String,
    type: String,
    size: Number,
    bucket: String,
    filename: String
})

const blogPostSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true
    },
    slug: {
        type: String,
        // required: true,
        unique: true,
        slug: 'title',
        set: (value) => {
            if (!value) {
                throw new Error("Slug must not be empty")
            }
            return value
        },
    },
    coverImage: CoverImage,
    content: {
        type: String,
        required: true,
        set: (value) => sanitizeHtml(value),
    },
    category: {
        type: String,
        required: true,
        default: "INSPECTORAMA",
        enum: BLOG_CATEGORIES
    },
    author: {
        type: mongoose.Schema.Types.ObjectId,
        required: true,
        ref: "User"
    },
    isPinned: {
        type: Boolean,
        default: false
    },
    status: {
        type: String,
        required: true,
        enum: BLOG_STATUSES
    },
},
    {
        timestamps: true
    })

blogPostSchema.pre("save", async function (next) {
    if (this.isPinned && this.status != "PUBLISH") {
        throw new Error("You can only pin a published post")
    }

    if (this.isPinned) {
        //Unpin all other pinned posts
        await BlogPost.updateMany({
            isPinned: true
        }, {
            isPinned: false
        })
    }
    next()
})

blogPostSchema.pre('findOneAndUpdate', async function (next) {
    // Access the updated document using `this`

    const newBlogPost = this.getUpdate().$set

    if (newBlogPost.isPinned && newBlogPost.status != "PUBLISH") {
        throw new Error("You can only pin a published post")
    }

    if (newBlogPost.isPinned) {
        //Unpin all other pinned posts
        await BlogPost.updateMany({
            isPinned: true
        }, {
            isPinned: false
        })
    }

    next()
});

blogPostSchema.methods.toJSON = function () {
    const object = this.toObject()
    if (object.coverImage) {
        object.coverImage.fileUrl = getSignedS3Url(object.coverImage.key)
    }
    return object
}

const BlogPost = mongoose.model('blog post', blogPostSchema)

export default BlogPost

Hello @eakenbor,

:eyes: You can add as many models as you want. Mongoose v8.0.0: Models

The following pattern works normally, but this is more of a mongoose issue.

connection.model("ModelName", schema)
connection.model("ModelName2", schema2)
...

Are you talking about automatically creating models based on actual data in your No-SQL DB?

Have you use forest CLI to generate your project ? I
:bulb: It creates it for you. (You can try it using Advanced Onboarding setup and following the Mongo DB setup)

How does it create them.

index.js

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

const connection = Mongoose.createConnection(process.env.DATABASE_URL);

fs
  .readdirSync(__dirname)
  .filter((file) => file !== 'index.js')
  .forEach((file) => {
    try {
      const { schema, modelName, collectionName } = require(path.join(__dirname, file));
      connection.model(modelName, schema, collectionName);
    } catch (error) {
      console.error(`Model creation error: ${error}`);
    }
  });

module.exports = connection;

model.js

const Mongoose = require('mongoose');

const schema = new Mongoose.Schema({
  address: {
    streetNumber: Number,
    streetName: String,
    city: String,
    country: String,
  },
  lastname: String,
  firstname: String,
  storeId: Number,
  bills: [{
    title: String,
    amount: Number,
    issueDate: Date,
    items: [{
      importance: String,
      title: String,
      amount: Number,
    }],
  }],
}, {
  timestamps: false,
});

module.exports = {
  collectionName: 'accounts',
  modelName: 'accounts',
  schema,
};

If you are talking without defining any models at all.

This is something we are working on but No-SQL is not that straightforward to create the models from since the data can be anything. We have some ideas on how to solve this (basically taking a sample of the DB and analyzing it) but it will take us some more time to finalize it.

I see that you’ve overridden the default toJSON blogPostSchema.methods.toJSON method. It could be the cause. :man_shrugging:
(Or the fact that you refer to User and it doesn’t look to be declared following your first message :eyes:)

In general in forest admin, you should prefer using Computed field to do this. It allows computing asynchronous resources (in an optimized way) instead of doing the computation one by one.

You should have more logs in your agent (backend), if not you can enable debugging with loggerLevel: 'Debug', (more in info about logger options)

Kind regards,
Morgan