Creating simple blog with Node.js (express and mongodb)

Libraries toolkits used in this project:

  • jade
  • express
  • mongojs
  • twitter-bootstrap
  • momentjs

Let’s start creating new blog project by creating the empty express project.

express blog

The output should look like this:

create : blog
create : blog/package.json
create : blog/app.js
create : blog/public
create : blog/routes
create : blog/routes/index.js
create : blog/views
create : blog/views/layout.jade
create : blog/views/index.jade
create : blog/public/stylesheets
create : blog/public/stylesheets/style.css
create : blog/public/javascripts
create : blog/public/images

dont forget to install dependencies:
$ cd blog && npm install

Requirements for our simple blog(must contain the CRUD):

  • Add new blog posts (Create).
  • View the list of all our posts, order by latest create date (Read, there should be separate “full text” read).
  • All make mistakes, we need to make the post editable by author, it would be good to use create form (Update).
  • Remove the posts, maybe not delete but also change state to unpublished, but also provide the delete (Delete).
  • Commenting in separate post view.
  • Login for blog owner and for publishers.
The design and web structure will be created with the twitter bootstrap library, simple way to create good looking site with small amount of time.

Lets create basic structure of our mongo data model.

var post = {
subject: 'This the test subject'
, body: 'Body should contain the markdown content'
, tags: [ 'first', 'mongodb', 'express']
, created: new Date()
, modified: new Date()
, state: 'published'
, author: {
username: 'estveeb'
}
, comments: [
{
name: 'Test user'
, body: 'I like your blog'
, created: new Date()
}
]
};

Next thing is to add the simple wrapper library to communicate mongodb, called Mongojs. To start the connection to database add this to app.js and then add the posts query to your router. Run the node app.js and open browser type localhost:3000.

db = require('mongojs').connect('blog', ['post']);
...
app.get('/', function(req, res) {
  var fields = { subject: 1, body: 1, tags: 1, created: 1, author: 1 };
  db.post.find({ state: 'published'}, fields, function(err, posts) {
    if (!err && posts) {
      res.render('index.jade', { title: 'Blog list', postList: posts });
    }
  });
});


Showing individual blog post, we will be using route param pre condition which will be accessed before going through route itself. In our case we are making the request to database to make sure the post exists.

// Route param pre condition
app.param('postid', function(req, res, next, id) {
  if (id.length != 24) return next(new Error('The post id is not having correct length'));

  db.post.findOne({ _id: db.ObjectId(id) }, function(err, post) {
    if (err) return next(new Error('Make sure you provided correct post id'));
    if (!post) return next(new Error('Post loading failed'));
    req.post = post;
    next();
  });
});

app.get('/post/:postid', function(req, res) {
  res.render('show.jade', {
    title: 'Showing post - ' + req.post.subject,
    post: req.post
  });
});


In the blog post view we need to add the fields for commenting(name, comment) and handle the post request if some of the comments is posted. For security reason there is need to check if the blog post is in the post’s collections or not, in my code i haven’t added.

// Add comment
app.post('/post/comment', function(req, res) {
  var data = {
      name: req.body.name
    , body: req.body.comment
    , created: new Date()
  };
  db.post.update({ _id: db.ObjectId(req.body.id) }, {
    $push: { comments: data }}, { safe: true }, function(err, field) {
      res.redirect('/');
  });
});

The functionality without authentication is covered now we need to add the auth also, for adding new posts, editing and deleting. We will implement really basic auth system the user has username, password which are sent to server compare the hashes and give user access or not. I don’t like examples which use plain password fields so we use sha256 + salt to generate hashes.
The routes which need to have authenticated user, has middleware check for access rules.

// Login
app.get('/login', function(req, res) {
  res.render('login.jade', {
    title: 'Login user'
  });
});

app.get('/logout', isUser, function(req, res) {
  req.session.destroy();
  res.redirect('/');
});

app.post('/login', function(req, res) {
  var select = {
      user: req.body.username
    , pass: crypto.createHash('sha256').update(req.body.password + conf.salt).digest('hex')
  };

  db.user.findOne(select, function(err, user) {
    if (!err && user) {
      // Found user register session
      req.session.user = user;
      res.redirect('/');
    } else {
      // User not found lets go through login again
      res.redirect('/login');
    }

  });
});


Now the adding post functionality, which checks the user state logged in or not and then inserts data.

app.get('/post/add', isUser, function(req, res) {
  res.render('add.jade', { title: 'Add new blog post '});
});

app.post('/post/add', isUser, function(req, res) {
  var values = {
      subject: req.body.subject
    , body: req.body.body
    , tags: req.body.tags.split(',')
    , state: 'published'
    , created: new Date()
    , modified: new Date()
    , comments: []
    , author: {
        username: req.session.user.user
    }
  };

  db.post.insert(values, function(err, post) {
    console.log(err, post);
    res.redirect('/');
  });
});

In the last version I added simple editing and deleting also. In edit we do the check if article exists and then render the editor, also we can fake the POST values so there is need for additional database query before starting $set.

app.get('/post/edit/:postid', isUser, function(req, res) {
res.render('edit.jade', { title: 'Edit post', blogPost: req.post } );
});

app.post('/post/edit/:postid', isUser, function(req, res) {
db.post.update({ _id: db.ObjectId(req.body.id) }, {
$set: {
subject: req.body.subject
, body: req.body.body
, tags: req.body.tags.split(',')
, modified: new Date()
}}, function(err, post) {
res.redirect('/');
});
});


Delete functionality has to be redone as it is not good idea to delete the posts with GET which parameters come from url, also no additional check if the user clicked it by accident, so there must be also some confirm buttons.

app.get('/post/delete/:postid', isUser, function(req, res) {
  db.post.remove({ _id: db.ObjectId(req.params.postid) }, function(err, field) {
    res.redirect('/');
  });
});

I have left out many features, because the post would have gone to long, there is no editing or deleting feature, also validating data and filtering. I will add the features later with some updates to my code, any major feature is going to be covered in my blog. Check out my github page for newer implementations.

About these ads

8 comments

  1. Hi,
    It’s really great but i m a newbie with node.js and don’t know where you put your post model file. Other thing, how do you do to start your applicaton ?
    Thanks.

  2. Sorry, I’m noobie in Node.js. Please, can you explain me what is the file name that you putted the basic structure of mongo data model? And then, where are the index.jade? How do I create this? Thank u in advance.

  3. Amazing! This blog looks just like my old one! It’s on a completely different topic but it has pretty much the same layout and design. Outstanding choice of colors!

  4. I think you save it all into one file called node. Not positive because it doesn’t say.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s