Frosty CMS: Setting up the Database 🗄️

Intro

Continuing our series on Frosty CMS, where we are building a blog CMS from scratch. In this post we will be setting up our database, which will be MongoDB, and we will be using Mongoose to help us interact with that database.

It is possible to use MongoDB as a service but for the time being in this application we will be installing it on the same server as our application. Installation follows the steps in our Mongo introduction. And the refresher on Mongoose installation can be found here: Introduction to MongooseJS.

Mongoose File Structure

One of the things not covered in our previous post on Mongoose is the file structure inside of an actual application. We will be writing a separate Javascript file that will contain our data schemas and functions, and we want to execute those in our Frosty application. We will not be putting our Mongoose code in our app.js file.

In the root of our environment we will make a new directory named Databases and in this directory will live each of our Javascript files which will perform a specific database action. Let’s start out with a file for posts, settings and users.

databases structure

We haven’t done any front-end work on settings or users yet. But we will eventually!

Creating and Connecting Databases

Once MongoDB and Mongoose have been installed. We will use the Mongo shell to create three databases respectively titled:

  • frosty_posts
  • frosty_users
  • frosty_settings

and then we will follow our Mongoose instructions to import Mongoose into our posts.js application and use it to connect to the databases.

// import mongoose
var mongoose = require("mongoose");

// connecting application to mongoDB
mongoose.connect('mongodb://localhost/frosty_posts', {useNewUrlParser: true, useUnifiedTopology: true});
mongoose.connect('mongodb://localhost/frosty_settings', {useNewUrlParser: true, useUnifiedTopology: true});
mongoose.connect('mongodb://localhost/frosty_users', {useNewUrlParser: true, useUnifiedTopology: true});

var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
  console.log("MongoDB Connected");
});

If everything has gone well here we should get “MongoDB Connected” in the console when we restart the app. If we goofed we’ll get “connection error”.

mongo db connected

Data Schemas

Even though our database is non-relational, we still want to have a data schema for validation and retrieval. Let’s take a look at what we would put in our posts.js file:

// MongoDB Schema for Posts
var postSchema = new mongoose.Schema({
    image: String,
    title: String,
    author: String,
    content: String
});

// creating schema variable named Post to be called later
// IE Post.find() or Post.create()
var Post = mongoose.model("Post", postSchema);

So we have created the schema and then compiled the schema to a mongoose model.

Saving Sample Post to Database

.save() method

Now that we have set all this up, let’s try saving a sample post to the frosty_posts database. In our same posts.js app we can quickly create an object called post1 that is filled with sample data, and then we use the .save method to save that object to the database, with a callback function that lets us know if it was successful or not.

// adding a post to the DB, first we create the data and put it into an object
var post1 = new Post({
    image: "https://ncoughlin.com/wp-content/uploads/2020/01/F4BB34AF-6E6F-4203-A1F0-321F9319A962_1_105_c.jpeg",
    title: "First Post in MongoDB",
    author: "Nick Coughlin",
    content: "Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo."
});

// then we save that object to the database, and add some console feedback
post1.save(function(err, post){
    if(err){
        console.log("Failed to write post to database.")
    } else {
        console.log("Post successfully saved to database.")
        console.log(post);
    }
});

Note that post1 here is just the variable that we are using to store the object and then run the .save method on. This is a totally arbitrary variable name. It’s not being saved to the database, it’s just being used on the Javascript side of things to apply the method to the object. Another name for this could have been newPost or capybaras. It’s totally arbitrary.

And if we run that application we get the following:

post successfully saved to database

.create() method

The previous .save() method required two steps, where we save our post object to a variable and then .save() that variable to the database. There is another method where we can do all of this in one step called .create().

// adding a post to the DB in one go with the .create() method
Post.create({
    image: "https://ncoughlin.com/wp-content/uploads/2020/01/F4BB34AF-6E6F-4203-A1F0-321F9319A962_1_105_c.jpeg",
    title: "Training Capybaras",
    author: "Steve Buscemi",
    content: "Always remember to bring a bag of lettuce."
}, function(err, post){
    if(err){
        console.log("Failed to write post to database.");
    } else {
        console.log("Post successfully saved to database.");
        console.log(post);
    }
});

and if we run the program again:

create method success

Transferring Database Setup to Primary App

Now that we have this schema and sample working in our temporary app, let’s transfer this over to the primary App. We will simply repeat the setup steps in our main app.js file so that we have added the following:

// ***************************
// DATABASE SETUP
// ***************************

// connecting application to mongoDB
mongoose.connect('mongodb://localhost/frosty_posts', {useNewUrlParser: true, useUnifiedTopology: true});
mongoose.connect('mongodb://localhost/frosty_settings', {useNewUrlParser: true, useUnifiedTopology: true});
mongoose.connect('mongodb://localhost/frosty_users', {useNewUrlParser: true, useUnifiedTopology: true});

// console logging if connection is successful
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
  console.log("MongoDB Connected");
});

// Mongoose Schema for Posts
var postSchema = new mongoose.Schema({
    image: String,
    title: String,
    author: String,
    content: String
});

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

Eventually we will move our Schema to a separate location so that it doesn’t bog down this file, but for the time being this is where it will live. The next step is to make it so that our posts.ejs template is being populated by the database data instead of the static array. We will save that for the next post: Frosty CMS: Populating Template Content From the Database

GitHub Repo

Ncoughlin: Frosty