Skip to content

Add blog likes and comments support (v3 API alignmen)#180

Open
evulins wants to merge 12 commits into
devfrom
add-api-for-blog-likes-comments
Open

Add blog likes and comments support (v3 API alignmen)#180
evulins wants to merge 12 commits into
devfrom
add-api-for-blog-likes-comments

Conversation

@evulins
Copy link
Copy Markdown
Collaborator

@evulins evulins commented Dec 23, 2025

Description

Add blog likes and comments support aligned with v3 API (auth-required POSTs, content-based comments body, unified response parsing).

Type of Change

  • New feature
  • Bug fix
  • Breaking change
  • Documentation update

Checklist

  • Unit/integration tests
  • Documentation

Tickets

@evulins evulins changed the title Add blog likes and comments support (v3 API alignmen Add blog likes and comments support (v3 API alignmen) Jan 7, 2026
Copy link
Copy Markdown
Collaborator

@hanka hanka left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some comments make be not relevant if some solutions are driven by requirements I'm not aware of :)

Comment thread src/models/comment.js
},
timestamps: { createdAt: 'date_created', updatedAt: 'date_edited' },
},
);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

probably worth adding an index on postId, so querying comments per post is better optimised

CommentSchema.index({ postId: 1 });

Comment thread src/controllers/comment.js Outdated
comment.postId = new mongoose.Types.ObjectId(postId);
comment.userId = userId;
comment.content = cleaned;
comment.author = authorName;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curiosity - only logged in users can comment, and they are assigned to comments. Why the need to keep author separately and additionally set it to 'Anonymous' when the param is blank? Why not just use relation to the user?

Comment thread src/controllers/comment.js Outdated
comment.author = authorName;

const savedComment = await comment.save();
const populatedComment = await Comment.findById(savedComment._id)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this additional query needed? maybe await savedComment.populate('userId', 'first_name last_name email'); would be enough?

Comment thread src/models/comment.js
},
content: {
type: String,
required: [true, 'A comment must have content'],
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could add also length validation on model level, like maxlength: [1000, 'Comment must be under 1000 characters'],

Comment thread src/controllers/comment.js Outdated
Comment on lines +88 to +104
// Get user from JWT
let user;
try {
user = await getUserByJWT(req.headers.authorization);
} catch (error) {
return {
...RESPONSE_CODES.UNAUTHORIZED,
error: { message: 'User must be logged in to comment' },
};
}

if (!user) {
return {
...RESPONSE_CODES.UNAUTHORIZED,
error: { message: 'User must be logged in to comment' },
};
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could probably be simplified to sth like this

let user;
try {
  user = await getUserByJWT(req.headers.authorization);
  if (!user) throw new Error('Invalid token');
} catch (error) {
  return {
    ... RESPONSE_CODES.UNAUTHORIZED,
    error: { message:  'User must be logged in to comment' },
  };
}

Additionally, consider checking logged in user at the beginning of createComment. There's not much sense in validating params or doing any additional db requests if there's no authorised user.

General improvement thought - probably check for logged in user is done in other controllers too. Maybe this could be extracted as a helper function or even a middleware applied on the router level, that could be reused. So error handling for unauthorised users could be unified.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if you'd like to use middleware for auth, here's blog post with example of how to do that: https://embed17.medium.com/protect-your-express-js-routes-a-simple-authentication-middleware-tutoria-cd720ef19cb3

Comment thread src/controllers/like.js
const likes = await Like.find({ postId })
.populate('userId', 'first_name last_name email');

let userHasLiked = false;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curiosity - returned likes are populated with user, so client using this controller's response should know whether it has logged in user or not, and if it has, it could check if current user has liked given post based on users returned with likes. Is this additional userHasLiked needed?

Comment thread src/controllers/like.js Outdated

if (existingLike) {
await Like.deleteOne({ _id: existingLike._id });
const remainingLikes = await Like.find({ postId });
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this could be optimised using const count = await Like.countDocuments({ postId });

Comment thread src/controllers/like.js Outdated
...RESPONSE_CODES.SUCCESS,
data: {
liked: true,
count: allLikes.length,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here too, using countDocuments would avoid loading all likes data just to count them.

Comment thread src/controllers/like.js Outdated
Comment on lines +71 to +86
let user;
try {
user = await getUserByJWT(req.headers.authorization);
} catch (error) {
return {
...RESPONSE_CODES.UNAUTHORIZED,
error: { message: 'User must be logged in to like a post' },
};
}

if (!user) {
return {
...RESPONSE_CODES.UNAUTHORIZED,
error: { message: 'User must be logged in to like a post' },
};
}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logged in user check is basically the same as in the comment controller - having it extracted to a helper would simplify controllers

Comment thread src/controllers/like.js
* @param {Object} req request object (used to get user from JWT)
* @returns {Promise<Object>} promise that resolves to like/unlike result or error
*/
export const toggleLike = async (postId, req) => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whole toggle could probably be a bit simplified to sth like this:

export const toggleLike = async (postId, req) => {
  // auth check
  // existing post check
  
  const deleted = await Like.findOneAndDelete({ postId, userId });
  
  if (deleted) {
    const count = await Like.countDocuments({ postId });
    return { ... RESPONSE_CODES.SUCCESS, data: { liked: false, count } };
  }
  
  try {
    await Like.create({ postId, userId });
    const count = await Like.countDocuments({ postId });
    return { ...RESPONSE_CODES.SUCCESS, data: { liked: true, count } };
  } catch (error) {
    // current error handling
  }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants