-2

I new to this stuff but I have a basic nodejs blog app. I knew how to create a new post and do the CRUD but I also want to count how many times a post was viewed. There is no registration so I just try to tell the app the each time the url was hit like localhost:3000/post/{{id}} the counter should goes up by one.

I have a simple logic but I can't save it to database. I check it in Compass and view count remains 0.

This is the post route:

router.get('/post/:slug', (req, res) => {
    Post.findOne({slug: req.params.slug})
        .populate({path: 'comments', populate: {path: 'user', model: 'users'}})
        .populate('user')
        .then(post => {
            let counter = req.body.viewCount
            counter++
            counter.save()
            Category.find({})
                .then(categories => {
                    res.render('home/post', {post: post, categories: categories});
                });
        });
});

module.exports = router; 

And the model (the view count part):

viewCount: {
        type: Number,
        default: 0
    },
  • first of all, yo didn't specify what ORM you're using, or what DB we're working with here. It looks to me a little like it could be Mongoose (with MongoDB, but this is just my guess) That makes it rather difficult to start guessing what might be the right solution. Second of all most I/O operations in Node.js are async, and therefore I'm guessing that `counter.save()` returns a promise, that you're not awaiting here, hence chances are it is never actually executed. – Kamil Janowski Oct 17 '21 at 08:19
  • Third of all... `req.body.viewCount`? You allow the number to come from the request? I mean... that's cool... but if I made a blog post I would want to make sure that it has as high views number as possible and I would forge my request manually and send `viewCount` equal to 1 billion :D and you would just take it, increment by 1 and save it... :D Most DBs offer an option to increment the value by 1 atomically without even loading the value to memory. You probably should use that. I could point you to the right place if I knew what frameworks you're actually using – Kamil Janowski Oct 17 '21 at 08:22
  • Yes it is mongoose and mongodb. I am just on this thing since forever. – exitcode_1 Oct 17 '21 at 08:23
  • Is there a way to do it? I just want to do it simple. – exitcode_1 Oct 17 '21 at 09:10

1 Answers1

0

right, so if you want to increment a value in mongodb, I would like to recommend you a similar post: How do I increment a Number value in Mongoose? (or you know... any random blog or official documentation )

My educated guess on how this would apply to your code is the following

router.get('/post/:slug', async (req, res) => {
    await Post.findOneAndUpdate({slug : req.params.slug}, {$inc : {'counter' : 1}});
    await Post.findOne({slug: req.params.slug})
        .populate({path: 'comments', populate: {path: 'user', model: 'users'}})
        .populate('user')
        .then(post => {
            return Category.find({})
                .then(categories => {
                    res.render('home/post', {post: post, categories: categories});
                });
        });
});

(note the async and await. These are more modern equivalents of .then() and .catch() that you still appear to be using)

Having said that, while this would be your MVP, you probably want to consider setting some cookies every time somebody visits the post. This way the cookie will be sent to your browser with every follow up request and you can make sure that a user cannot add up a million views simply by refreshing the website over and over again :P Optionally same thing can be achieved by storing IPs the requests were made from.

Kamil Janowski
  • 1,872
  • 2
  • 21
  • 43