Check out bidbear.io Amazon Advertising for Humans. Now publicly available 🚀

Frosty CMS: Seeding the Database 🌱

Intro

Continuing our series on Frosty CMS, where we are building a blog CMS from scratch. Last time we started to refactor our code by creating local modules. This time we are going to go over the process of seeding the database.

Seeding is when we wipe the database and then add a set of filler data, which makes it easy to test when we are making changes. It’s sort of like Lorem Ipsum, for the database. This will be useful for us as we go through the process of adding comments to our blog, which is next in the pipeline.

File Structure

Like any application in node, our database seeding application will need to be a Javascript file, and it will live in the main index. We will call it seeds.js.

Our application needs to interact with our database, so we must import mongoose, and we also needs to manipulate our Blog data, so we import our Blog module.

const express          = require("express"),
      Blog             = require("./models/blogs");

Model.remove()

Then we need to delete all of the Blog data currently in the database, otherwise we will just keep stacking the seed data on top of itself. To do this we will use the Mongoose Model.deleteMany() method.

// remove all blog data
Blog.deleteMany({}, function(err){
    if(err){
        console.log(err);
    } else {
        console.log("ALL BLOG DATA DELETED");
    }
})

Convert to Function

To run this code we will need to make this a function, turn the application into a module and then import and run the function in our main application. We can start by inserting our deletion code into a function named seedDB and export it as a module.

// remove all blog data function
function seedDB(){
    Blog.deleteMany({}, function(err){
        if(err){
            console.log(err);
        } else {
            console.log("ALL BLOG DATA DELETED");
        }
    });
}

// export as module    
module.exports = seedDB;

and then in our primary application we import our seedDB module and run the seedDB function.

// import modules
const express          = require("express"),
      app              = express(),
      bodyParser       = require("body-parser"),
      mongoose         = require("mongoose"),
      methodOverride   = require("method-override"),
      expressSanitizer = require('express-sanitizer'),
      Blog             = require("./models/blogs"),
      seedDB           = require("./seeds");
      
// run seed database function
seedDB();

and if we run the application we will see that all of our blog data has been deleted. Terrifying. Let’s work on the process of adding seed data in now.

Adding Seed Data

To start let us add a static array of content and set it to a variable in seeds.js file.

// static data array to seed database
const data = [
    {
    image: 'https://ncoughlin.com/wp-content/uploads/2020/01/F4BB34AF-6E6F-4203-A1F0-321F9319A962_1_105_c.jpeg',
    title: 'Strange Brazilian Dogs',
    author: 'Carl Sagan',
    date: 2020-03-18,
    short: 'They have an unusual bark, and a fondness for water.',
    content:
   'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
    },{
    image: 'https://ncoughlin.com/wp-content/uploads/2020/01/C3189616-0913-42F9-BE4E-CF41153032D5_1_105_c.jpeg',
    title: 'Piles Of Small Rocks',
    author: 'David Attenborough',
    date: 2020-03-18,
    short: 'People really seem to like these piles of small rocks littered near the ocean.',
    content:
   'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
    },{
    image: 'https://ncoughlin.com/wp-content/uploads/2020/01/A7A76DA3-359B-4918-9020-C7D9BB5F43C2_1_105_c.jpeg',
    title: 'Toys For Adults',
    author: 'Jeremy Clarkson',
    date: 2020-03-18,
    short: 'A wildly unsafe way to enjoy your retirement.',
    content:
   'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
    },{
    image: 'https://ncoughlin.com/wp-content/uploads/2020/01/EC300587-85E0-40F7-B36C-C3B66F6C9373_1_105_c.jpeg',
    title: 'Traditional Juice Sticks',
    author: 'Gordan Ramsey',
    date: 2020-03-18,
    short: 'Piles of sugarcane in a truck.',
    content:
   'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
    },{
    image: 'https://ncoughlin.com/wp-content/uploads/2020/01/3F1A1C75-AC5F-4227-A722-0014D294872D_1_105_c.jpeg',
    title: 'High Stakes Jungle Gym',
    author: 'David Hasselhoff',
    date: 2020-03-18,
    short: 'It\'s a modern style greenhouse for cacti.',
    content:
   'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
    }
    
]

And then we need to loop over this array called data and create a new object in the mongo model Blog for each item in the database. We will place all of this inside of the callback of the delete function. We place it in the callback instead of separately because if the data does not delete successfully, we don’t want to add more. That will all look like this.

// remove all blog data function
function seedDB(){
    Blog.deleteMany({}, function(err){
        if(err){
            console.log(err);
        } else {
            console.log("ALL BLOG DATA DELETED");
             // add seed data
             data.forEach(function(seed){
                Blog.create(seed, function(err, data){
                    if(err){
                        console.log(err);
                    } else {
                        console.log("BLOG DATA ADDED");
                    }
                });
            });
        }
    });
}

We test that and it works. Now every time we restart the application all of the blog data will be reset to this seed data. Which makes it easy to test functions like editing and deleting without having to manually add the blog data back. Now we will go through the same process for some comments.

Adding Seed Data for Comments

On a side note, this next process that we will be going through to add the comment seed data is called “error driven development”. Because we will be referencing a model that doesn’t exist, and we will get an error, and we will solve that error. We will continue that process until it works!

So here in our callback for Blog.create() we have added a comment using the mongoose model Comment which does not exist yet.

// remove current blog data
function seedDB(){
    Blog.deleteMany({}, function(err){
        if(err){
            console.log(err);
        } else {
            console.log("ALL BLOG DATA DELETED");
             // add seed data
             data.forEach(function(seed){
                Blog.create(seed, function(err, data){
                    if(err){
                        console.log(err);
                    } else {
                        console.log("BLOG DATA ADDED");
                        // create comment
                        Comment.create(
                            {
                                author: "Duke Ellington",
                                text: "This comment will be the same for every blog. But it's just seed data so who cares."
                            }, function(err, comment){
                            });
                    }
                });
            });
        }
    });
}

We are really getting into callback hell now, but don’t worry, we will refactor this.

And as expected we get an error that Comment is undefined.

Comment is not defined

Lastly we need to push the comment onto the Blog and save the Blog.

// remove current blog data
function seedDB(){
    Blog.deleteMany({}, function(err){
        if(err){
            console.log(err);
        } else {
            console.log("ALL BLOG DATA DELETED");
             // add seed data
             data.forEach(function(seed){
                Blog.create(seed, function(err, blog){
                    if(err){
                        console.log(err);
                    } else {
                        console.log("BLOG DATA ADDED");
                        // create comment
                        Comment.create(
                            {
                                author: "Duke Ellington",
                                text: "This comment will be the same for every blog. But it's just seed data so who cares."
                            }, function(err, comment){
                                if(err){
                                    console.log(err);
                                } else {
                                    blog.comments.push(comment);
                                    blog.save();
                                    console.log("created new comment");
                                }
                            });
                    }
                });
            });
        }
    });
}

Solving Errors

Our biggest error right now is that Comment (mongoose schema) is not defined. It doesn’t exist currently so that makes perfect sense. We need to define a Mongo Schema for comments and put it in a local module, and then reference that module in our seeds application.

Here is the comments schema:

const mongoose = require("mongoose");

// Mongoose Schema for comments
var commentSchema = new mongoose.Schema({
    author: String,
    date: {type: Date, default: Date.now},
    content: String
});

// creating schema Model named Blog to be called later
// IE Blog.find() or Blog.create()
// Compiling the model is what allows us to run these Mongoose methods
module.exports = mongoose.model("Comment", commentSchema);

And then we reference that in our seeds application:

const mongoose         = require("mongoose"),
      Blog             = require("./models/blogs"),
      Comment          = require("./models/comments");

Comment has now been created and referenced.

Refactoring with Async Await

We covered the basics of Async Await in another post. Now lets apply those principles to this mini seeds application to get ourselves out of callback hell.

// Async version: remove current blog data
async function seedDB(){
    try {
        await Comment.deleteMany({});
        await Blog.deleteMany({});
        for (const blogSeed of  blogSeeds) {
            let blog = await Blog.create(blogSeed);
            let comment = await Comment.create(
                                {
                                    author: "Duke Ellington",
                                    text: "This comment will be the same for every blog. But it's just seed data so who cares."
                                }
                            );
        blog.comments.push(comment);  
        blog.save();
        }
        } catch (err) {
            console.log(err);
        }
console.log("Blog Data Removed and Re-Seeded");
}

A couple of things to note here, we are passing in blogSeed on Blog.create from the static array of blog content that we defined earlier.

And on blog.comments.push this will not work until we have added comments to the Blog schema. Which looks like this:

// Mongoose Schema for Blogs
var blogSchema = new mongoose.Schema({
    image: String,
    title: String,
    author: String,
    date: {type: Date, default: Date.now},
    short: String,
    content: String,
    comments: [
      {
         type: mongoose.Schema.Types.ObjectId,
         ref: "Comment"
      }
    ]
});

Seeding Multiple Comments For Each Blog

This worked great to add one comment to each blog post. But what if we want to add multiple? We just need to put our comments in their own static array and make a nested loop that pushes each comment in the array to each blog like this.

const commentSeeds = [
    {
        author: "Duke Ellington",
        content: "This comment will be the same for every blog. But it's just seed data so who cares."
    },{
        author: "Marv Ellis",
        content: "Would you be interested in hosting a guest post about article xyz?"
    },{
        author: "Ella Fitzgerald",
        content: "Just don't give up trying to do what you really want to do. Where there is love and inspiration, I don't think you can go wrong."
    }
];


// Async version: remove current blog data
async function seedDB(){
    try {
        await Comment.deleteMany({});
        await Blog.deleteMany({});
        console.log("All Blogs and Comments Deleted");
        for (const blogSeed of  blogSeeds) {
            let blog = await Blog.create(blogSeed);
            for (const commentSeed of commentSeeds) {
                let comment = await Comment.create(commentSeed);
                blog.comments.push(comment);  
                }
        blog.save();        
        console.log("Blog Saved"); 
        }
        } catch (err) {
            console.log(err);
        }
}

multiple comments

GitHub Repo

Ncoughlin: Frosty

Amazon Ad Analytics For Humans

Advertising reports automatically saved and displayed beautifully for powerful insights.

bidbear.io
portfolios page sunburst chart