POST /api/likes
Toggles upvote or downvote status for a question or answer. The endpoint uses atomic operations to ensure vote consistency.
Authentication
Valid NextAuth session token required
Request Body
ID of the question or message to vote on
Type of target. Options:
question - Vote on a question
message - Vote on an answer/message
Type of vote. Options:
up - Upvote (like)
down - Downvote
Response
Action performed. Possible values:
liked - Item was upvoted
unliked - Upvote was removed
downvoted - Item was downvoted
undownvoted - Downvote was removed
Total number of upvotes after the operation
Total number of downvotes after the operation
Whether the current user has liked this item
Whether the current user has downvoted this item
Vote Behavior
The API implements intelligent vote toggling:
Show Upvote Logic (voteType='up')
- Already upvoted: Remove upvote (unlike)
- Decrements upvote count
- Removes user from likedBy array
- Returns
action: "unliked"
- Currently downvoted: Switch to upvote
- Decrements downvote count
- Increments upvote count
- Moves user from dislikedBy to likedBy
- Returns
action: "liked"
- No existing vote: Add upvote
- Increments upvote count
- Adds user to likedBy array
- Returns
action: "liked"
Show Downvote Logic (voteType='down')
- Already downvoted: Remove downvote
- Decrements downvote count
- Removes user from dislikedBy array
- Returns
action: "undownvoted"
- Currently upvoted: Switch to downvote
- Decrements upvote count
- Increments downvote count
- Moves user from likedBy to dislikedBy
- Returns
action: "downvoted"
- No existing vote: Add downvote
- Increments downvote count
- Adds user to dislikedBy array
- Returns
action: "downvoted"
Example Request - Upvote
{
"targetId": "q_123",
"targetType": "question",
"voteType": "up"
}
Example Response - Upvoted
{
"action": "liked",
"upvotes": 16,
"downvotes": 2,
"liked": true,
"downvoted": false
}
Example Request - Downvote
{
"targetId": "msg_456",
"targetType": "message",
"voteType": "down"
}
Example Response - Downvoted
{
"action": "downvoted",
"upvotes": 8,
"downvotes": 3,
"liked": false,
"downvoted": true
}
Example Response - Switched Vote
{
"action": "liked",
"upvotes": 9,
"downvotes": 2,
"liked": true,
"downvoted": false
}
When switching from downvote to upvote (or vice versa), both counters are updated atomically in a single database operation.
Error Responses
Show 400 Bad Request - Missing Parameters
{
"error": "Missing parameters"
}
Returned when targetId or targetType is missing.
Show 400 Bad Request - Invalid Target Type
{
"error": "Invalid target type"
}
Returned when targetType is not ‘question’ or ‘message’.
Show 400 Bad Request - Invalid Vote Type
{
"error": "Invalid vote type"
}
Returned when voteType is not ‘up’ or ‘down’.
{
"error": "Unauthorized"
}
Returned when no valid session is found.
Returned when the specified question or message doesn’t exist.
Show 500 Internal Server Error
{
"error": "Like failed"
}
Returned when the database operation fails.
Atomic Operations
All vote operations are performed atomically using MongoDB’s updateOne with conditional queries. This ensures:
- No race conditions between concurrent votes
- Vote counts remain consistent
- User can only have one type of vote (upvote OR downvote) at a time
Usage Example
# Upvote a question
curl -X POST "https://api.example.com/api/likes" \
-H "Content-Type: application/json" \
-H "Cookie: next-auth.session-token=..." \
-d '{
"targetId": "q_123",
"targetType": "question",
"voteType": "up"
}'
# Downvote an answer
curl -X POST "https://api.example.com/api/likes" \
-H "Content-Type: application/json" \
-H "Cookie: next-auth.session-token=..." \
-d '{
"targetId": "msg_456",
"targetType": "message",
"voteType": "down"
}'
# Remove upvote (call again with same parameters)
curl -X POST "https://api.example.com/api/likes" \
-H "Content-Type: application/json" \
-H "Cookie: next-auth.session-token=..." \
-d '{
"targetId": "q_123",
"targetType": "question",
"voteType": "up"
}'
Database Storage
Vote data is stored directly on question and message documents:
upvotes (integer) - Total upvote count
downvotes (integer) - Total downvote count
likedBy (array of strings) - User IDs who upvoted
dislikedBy (array of strings) - User IDs who downvoted