Menu Home

How to build a blog with MongoDB, Express, Angular 2 and Node.js on Ubuntu – Part 2

Spread the love

In part 1 of this tutorial we setup our environment. In part 2, we’re going to write the necessary code to build a basic CRUD.

This will be a simple blog with posts and comments. Nothing fancy about it. Let’s begin

1. Automated build

As I showed in part 1 of this tutorial both express and Angular have their own build tools. Let’s make sure that every time the code changes we can simply refresh the browser to view the changes. Angular is easy, just initiate the build with –watch

ng build --watch true

now, every time you change something in your Angular app a build will run automatically.

To do the same with Express we will need to install a watcher. I’m using Nodemon. To install it, run this command in your terminal. Nodemon will be installed as a dev dependency as we only need it at dev time.

npm install --save-dev nodemon

Now that we have Nodemon installed, we can start the express server and leave it running watching for changes.

./node_modules/nodemon/bin/nodemon.js server.js

OK, this is not very easy to remember. Let’s add a new script in our package.json. In the scripts section add:

"dev": "nodemon server.js"

and then start Nodemon using npm

npm run dev

Now both Express (technically is node.js) and Angular are listening for our changes.

2. Define the routes.

First, we will define the routes. Our app needs to create a post, edit a post, delete a post, list all posts, leave a comment and delete a comment. Remember the blog.js file inside server/routes/

Remember the blog.js file inside ‘server/routes/api’? That is where we will add all of out back-end logic. Start by defining all the routes. Alter the file to look like this:

const express = require('express');
const router = express.Router();


/* GET blog listing. */
router.get('/', (req, res) => {
    res.json([]);
});

/* GET a given post */
router.get('/:postId/', (req, res) => {
    res.json([]);
});

/* POST a new post */
router.post('/', (req, res) => {
    res.json([]);
});

/* UPDATE a post */
router.put('/:postId/', (req, res) => {
    res.json([]);
});

/* DELETE a post */
router.delete('/:postId/', (req, res) => {
    res.json([]);
});

module.exports = router;

Now test your app. Using postman hit all the routes with the correct method (get, post, put, delete) and you should get something like this:

3. Make our life easier

Our app now works, and we can reach all the endpoints we need, now we need to start adding some logic to it.

To make our life easier we will add some code that we can reuse in the future. Just before the first route add:

const connection = (closure) => {
    return MongoClient.connect('mongodb://localhost:27017', (err, db) => {
        if (err) return console.log(err);

        closure(db);
    });
};

const sendError = (err, res) => {
    response.status = 501;
    response.message = typeof err == 'object' ? err.message : err;
    res.status(501).json(response);
};

let response = {
    status: 200,
    posts: [],
    message: null
};

We need to make sure that the MongoClient is included, add this piece of code at the top of your routes file

const MongoClient = require('mongodb').MongoClient;
const ObjectID = require('mongodb').ObjectID;

Let me explain: we need to access mongo more than once, the connection function establishes a mongo connection, it takes a closure as the first parameter and injects a mongo connection into it. This way we will be able to write our logic inside the closure not bothering about opening a connection every time.

Most likely, for some reason, our application will break, so we wrote a function to handle the errors.

Finally, The response object normalises the response so they all look the same.

If you try to reach your endpoint now it will look like this:

4. Manipulating data

We’ll start by adding some posts and then we can list them. I’m using an online tool to generate dummy body, there are several out there pick one or write your own body.

In postman, change the request type to “post” and the URL to http://localhost:3000/api/blog. Then click on the body tab under the URL and then tick “x-www-form-urlencoded”. Create two keys, one named name, the other named body, before submitting the request let’s build the logic to add a post.

Modify your “new post” route to look like this:

/* POST a new post */
router.post('/', (req, res) => {
    connection((db) => {
        let post = req.body;
        post.created_at = new Date();
        post.updated_at = null;
        post.comments = [];

        db.collection('blog').insert(post)
            .then((data) => {
                db.collection('blog').findOne({_id: data.insertedIds[0]})
                    .then((item) => {
                        response.posts = [item];
                        res.json(response);
                    });

            })
            .catch((err) => {
                sendError(err, res);
            });
    });
});

Now if you fill the keys you just created and hit send you will add a new entry on your mongo collection. In the response object, there will be an entry with the post you just created in the property posts:

Now that we can insert new posts, we need a way to list them. Change the body of the “blog listing” route to look like this:

/* GET blog listing. */
router.get('/', (req, res) => {
    connection((db) => {
        db.collection('blog')
            .find()
            .sort({created_at: -1}).toArray()
            .then((posts) => {
                response.posts = posts;
                res.json(response);
            })
            .catch((err) => {
                sendError(err, res);
            });
    });
});

and the “get a post” route to this:

/* GET a given post */
router.get('/:postId/', (req, res) => {
    connection((db) => {
        const id = new ObjectID(req.params.postId);

        db.collection('blog').findOne({_id: id})
            .then((post) => {
                response.posts = [post];
                res.json(response);
            })
            .catch(err => {
                sendError(err, res);
            })
    });
});

 

If you change the method back to get and click send or concatenate the _id property to the get URL and then click send you will get a response similar to this one:

Now change the “update a post” and “delete a post” routes to handle updates and deletes:

/* UPDATE a post */
router.put('/:postId/', (req, res) => {
    connection((db) => {
        const id = new ObjectID(req.params.postId);
        let post = req.body;
        post.updated_at = new Date();

        db.collection('blog').update({_id: id}, post)
            .then((result) => {
                db.collection('blog').findOne({_id: id})
                    .then((item) => {
                        response.posts = [item];
                        res.json(response);
                    });
            })
            .catch((err) => {
                sendError(err, res);
            });
    });
});

/* DELETE a post */
router.delete('/:postId/', (req, res) => {
    connection(db => {
        const id = new ObjectID(req.params.postId);
        db.collection('blog').deleteOne({_id: id})
            .then(data => {
                res.json(response);
            })
            .catch(err => sendError(err, res));
    });
});

That’s it, our CRUD is done. With all changes your file should look like this now:

const express = require('express');
const router = express.Router();
const MongoClient = require('mongodb').MongoClient;
const ObjectID = require('mongodb').ObjectID;


const connection = (closure) => {
    return MongoClient.connect('mongodb://localhost:27017', (err, db) => {
        if (err) return console.log(err);

        closure(db);
    });
};

const sendError = (err, res) => {
    response.status = 501;
    response.message = typeof err == 'object' ? err.message : err;
    res.status(501).json(response);
};

let response = {
    status: 200,
    posts: [],
    message: null
};

/* GET blog listing. */
router.get('/', (req, res) => {
    connection((db) => {
        db.collection('blog')
            .find()
            .sort({created_at: -1}).toArray()
            .then((posts) => {
                response.posts = posts;
                res.json(response);
            })
            .catch((err) => {
                sendError(err, res);
            });
    });
});

/* GET a given post */
router.get('/:postId/', (req, res) => {
    connection((db) => {
        const id = new ObjectID(req.params.postId);

        db.collection('blog').findOne({_id: id})
            .then((post) => {
                response.posts = [post];
                res.json(response);
            })
            .catch(err => {
                sendError(err, res);
            })
    });
});

/* POST a new post */
router.post('/', (req, res) => {
    connection((db) => {
        let post = req.body;
        post.created_at = new Date();
        post.updated_at = null;
        post.comments = [];

        db.collection('blog').insert(post)
            .then((data) => {
                db.collection('blog').findOne({_id: data.insertedIds[0]})
                    .then((item) => {
                        response.posts = [item];
                        res.json(response);
                    });

            })
            .catch((err) => {
                sendError(err, res);
            });
    });
});

/* UPDATE a post */
router.put('/:postId/', (req, res) => {
    connection((db) => {
        const id = new ObjectID(req.params.postId);
        let post = req.body;
        post.updated_at = new Date();

        db.collection('blog').update({_id: id}, post)
            .then((result) => {
                db.collection('blog').findOne({_id: id})
                    .then((item) => {
                        response.posts = [item];
                        res.json(response);
                    });
            })
            .catch((err) => {
                sendError(err, res);
            });
    });
});

/* DELETE a post */
router.delete('/:postId/', (req, res) => {
    connection(db => {
        const id = new ObjectID(req.params.postId);
        db.collection('blog').deleteOne({_id: id})
            .then(data => {
                res.json(response);
            })
            .catch(err => sendError(err, res));
    });
});

module.exports = router;

In part 3 of this tutorial, we will build the frontend to piece it all together. Go ahead, try out our brand new CRUD, add, update, list, delete as many times as you want. It will be useful in part 3.

Categories: Tutorials

Tagged as:

Claudio Pinto

Leave a Reply

Your email address will not be published. Required fields are marked *