Add blog likes and comments support (v3 API alignmen)#180
Conversation
hanka
left a comment
There was a problem hiding this comment.
Some comments make be not relevant if some solutions are driven by requirements I'm not aware of :)
| }, | ||
| timestamps: { createdAt: 'date_created', updatedAt: 'date_edited' }, | ||
| }, | ||
| ); |
There was a problem hiding this comment.
probably worth adding an index on postId, so querying comments per post is better optimised
CommentSchema.index({ postId: 1 });
| comment.postId = new mongoose.Types.ObjectId(postId); | ||
| comment.userId = userId; | ||
| comment.content = cleaned; | ||
| comment.author = authorName; |
There was a problem hiding this comment.
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.author = authorName; | ||
|
|
||
| const savedComment = await comment.save(); | ||
| const populatedComment = await Comment.findById(savedComment._id) |
There was a problem hiding this comment.
is this additional query needed? maybe await savedComment.populate('userId', 'first_name last_name email'); would be enough?
| }, | ||
| content: { | ||
| type: String, | ||
| required: [true, 'A comment must have content'], |
There was a problem hiding this comment.
you could add also length validation on model level, like maxlength: [1000, 'Comment must be under 1000 characters'],
| // 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' }, | ||
| }; | ||
| } |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
| const likes = await Like.find({ postId }) | ||
| .populate('userId', 'first_name last_name email'); | ||
|
|
||
| let userHasLiked = false; |
There was a problem hiding this comment.
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?
|
|
||
| if (existingLike) { | ||
| await Like.deleteOne({ _id: existingLike._id }); | ||
| const remainingLikes = await Like.find({ postId }); |
There was a problem hiding this comment.
this could be optimised using const count = await Like.countDocuments({ postId });
| ...RESPONSE_CODES.SUCCESS, | ||
| data: { | ||
| liked: true, | ||
| count: allLikes.length, |
There was a problem hiding this comment.
here too, using countDocuments would avoid loading all likes data just to count them.
| 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' }, | ||
| }; | ||
| } |
There was a problem hiding this comment.
logged in user check is basically the same as in the comment controller - having it extracted to a helper would simplify controllers
| * @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) => { |
There was a problem hiding this comment.
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
}
};
Description
Add blog likes and comments support aligned with v3 API (auth-required POSTs, content-based comments body, unified response parsing).
Type of Change
Checklist
Tickets