1 - MongoDB and CRUD

Welcome back for round 2. In the first post we downloaded some dependencies needed to run a basic server using express and node. In this tutorial we will be implementing some data persistence.

Looking towards what we want to get out of our “forum”, we want users to be able to create topics that add to the database. That’s where CRUD comes in. No, CRUD is not a substance that is disgusting or unpleasant, in this case anyway. CRUD stands for Create, Read, Update, Delete (or Destroy, if you will). They are the basic functions of persistent storage.

Storage refers to a database, which means that we need to use our MongoDB that we have running. If only we had something that could connect our NodeJS project to our database… lmao check out Mongoose:

$ npm install mongoose --save

We need to create the models for the data that we want to store in our datbase, we need to be able to Create, Read, Update, and Delete the records for this data, and we need to be able to do all of that through sending requests to our running app.

Our app structure currently looks like this:

- app
|  
- client
|
- config
|
- node_modules
|   |- A bunch of stuff
- package.json
- server.js

Navigate into the app folder and create three new folders: models, controllers, routes.

So our model is what defines the fields of our Topic object. The controller is designed to hold the functions that allow create, read, update, and destroy to occur. Routes will hold details on how to access these functions.

Creating a model for a forum topic

When creating models, we want to think about what we want out of the object we are creating. Thankfully, the mongoose package has an easy way to create models.

For a topic, the two basic properties I want is a title and a category. Let’s jump in.

This is what our topic.js file looks like:

// app/models/topic.js
// Load Mongoose Dependency
var mongoose = require('mongoose');

// Create the schema for our topic model
var TopicSchema = new mongoose.Schema({
    title: {type: String, required: true, index: {unique: true}},
    category: {type: String, required: true}
});

// export our model so it can be used elsewhere
module.exports = mongoose.model('Topic', TopicSchema);

In this file we are using the mongoose dependency to create a schema for our Topic model. Our topic schema has 2 fields at the moment: a title and a category. Both of these fields are of type String, and they are both required, meaning that we can create a new Topic that doesn’t have a title or a category. Only the title is unique, meaning that we cannot create two titles of the same name, but you can create multiple topics of the same category.

Creating a controller for our topic model

Our controller is what controls the persistence of data in our database. This is where we implement the creation, deletion, update, and read of our “Topic” (or any) model. In our controller folder, create “topic.js”. It is the same name as our topic model file, but that is fine since we are in different directories, plus in the long run it will help us stay organized.

// app/controllers/topic.js

// Import our topic model
var Topic = require('../models/topic');

// postTopic: creates a Topic and inserts into database
exports.postTopic = function(req, res){
    var topic = new Topic();
    topic.title = req.body.title;
    topic.category = req.body.category;
    
    topic.save(function(err) {
        if(err){
            res.send(err);
            return;
        }
        res.json({message: 'Topic added to database', data: topic});
    });
};

// getTopics: gets every topic in database
exports.getTopics = function(req, res) {
    Topic.find(function(err, topics) {
        if(err) {
            res.send(err);
            return;
        }
        res.json(topics);
    });
};

// getTopic: only gets topic of the particular topic_id
exports.getTopic = function(req, res) {
    Topic.findById(req.params.topic_id, function(err, topic) {
        if(err){
            res.send(err);
            return;
        }
        res.json(topic);
    });
};

// putTopic: updates a topic of given topic_id
exports.putTopic = function(req, res) {
    Topic.update({_id: req.params.topic_id}, { title: req.body.title, category: req.body.category }, function(err, num, raw){
        if(err){
            res.send(err);
            return;
        }
        res.json({message: num + ' updated'});
    });
};

// deleteTopic: deletes a topic of given topic_id
exports.deleteTopic = function(req, res){
    Topic.remove({_id: req.params.topic_id }, function(err){
        if (err){
            res.send(err);
            return;
        }
        res.json({message: 'Topic removed from database'});
    });
};

As you can see, in our code we have written functions to accomodate CRUD. Since we are using the mongoose package, and Topic is an implementation of a mongoose model, it has these functions build in to its class. Now all we need to do is link it with our app.

Routes

Within our app folder, create a new folder called routes. Within our routes folder, create a new file called topic.js.

Our routes folder will hold all of our routes for our router. We don’t want to mix up any routes so it is important to keep this organized. Here is what our file is going to look like:

// Import our dependencies and create our router for topics
var express = require('express');
var router = express.Router();

// Import our topic controller
var topicController = require('../controllers/topic');

// create /topics route
router.route('/topics')
    .post(topicController.postTopic)
    .get(topicController.getTopics);

// create /topics/:topic_id route
router.route('/topics/:topic_id')
    .get(topicController.getTopic)
    .put(topicController.putTopic)
    .delete(topicController.deleteTopic);

exports.topicRouter = router;

In this file we are creating a new router with two seperate routes. One for posting a topic and getting all topics, and one for getting, updating, and deleting a particular topic, hense the addition of the topic_id

Our last step is editing our server.js file to incorporate our app’s routes. We need to include mongoose and start up a connection with our mongodb. We also need to download and include another dependency: body-parser. This will allow our application to read the body contents of requests.

$ npm install body-parser --save
// updated server.js

// Dependencies
var express = require('express');
var http = require('http');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');

// Create our mongo connection
mongoose.connect('mongodb://localhost:27017/forum-example');

// Create our express app
var app = express();

// Create our router
var router = express.Router();

// Import our Routers
var topicRouter = require('./app/routes/topic');

// Tell our app to use our dependencies
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// tell our router to use our imported routes
router.use(topicRouter.topicRouter);

// Create our simple response
router.get('/', function(req, res) {
	res.json({message: "Hello, World!"});
});

// Tell our app to use our router ON THE API ROUTE
app.use('/api', router);

// Create our server using our app
var server = http.createServer(app);

// Tell our server to listen to requests
var ip = process.env.IP || "0.0.0.0";
var port = process.env.PORT || 3000;

server.listen(port, ip, function(){
	var addr = server.address();
	console.log("Generic server listening at", addr.address + ":" + addr.port);
});

Make sure that you’re running mongod before you start your server.

Notice that we have changed our app to use the main router on the “/api” route. This means that instead of requesting localhost:3000 to receive “Hello, World!”, we are actually requesting localhost:3000/api, like so:

Postman Api

Now we should be able to use our topic routes as well. Let’s send a get request to /api/topics: Get Topics

Ok so we receive an empty array, which is good since we haven’t inserted any Topics into our database. Let’s send a post request to api/topics with a body containing values of the title and category of the Topic we want to create: Post Topic

We have inserted a topic! Now when we send a get request we should see all of our topics: Get Topic Success

We can also update and delete the topic. For Getting/Updating/Deleting of a particular topic, we need to include topic_id in the url we are requesting. In our initial get request we can see the _id’s for all of our topics. Take the id and place it at the end of the url like so: Individual Get

Using this url with the appropriate topic_id, we can send a put request with different body values for title and cateogory to update the record: Updated

When we get again we can see that the record has changed: Changed

Now we delete it: Deleted

We can see we don’t have it in the database any longer: Empty

Ok we have our topic route all set, and we are done with our first model/controller/route combo!

Stay tuned for the next post where we will create another model for Users, but it is a little tricky because we will also have to implement authentication!

Cheers

Updated: