Skip to main content

Overview

Store Reviews API provides comprehensive review and rating functionality including customer reviews, store responses, helpfulness voting, review flagging, and review management. Location: convex/stores/reviews.ts

Verified Purchase Logic

Reviews do not require a verified purchase. Any authenticated customer can leave a review on any store. The isVerifiedPurchase flag is set automatically:
  • If orderId is provided: the flag is set to true (order must exist, belong to the customer, and be from the target store)
  • If orderId is omitted: the system checks if the customer has any delivered order from the store. If yes, isVerifiedPurchase is true; otherwise false
Reviews without a verified purchase are still created and visible — they simply lack the verified badge.

Add Review

Creates a new review for a store. Requires authentication. One review per order per customer (when orderId is provided).
storeId
Id<'stores'>
required
Store ID to review
orderId
Id<'orders'>
Optional order ID to link the review to a specific order. If provided, automatically sets isVerifiedPurchase to true.
rating
number
required
Rating from 1-5 stars
title
string
Review title (at least one of title or comment is required)
comment
string
Review comment (at least one of title or comment is required)
photoIds
Id<'_storage'>[]
Array of storage IDs for review photos
const review = await convex.mutation(api.stores.reviews.addReview, {
  storeId: "j123456789",
  orderId: "o123456789", // optional
  rating: 5,
  title: "Excellent food!",
  comment: "The pizza was amazing and delivery was fast. Highly recommended!",
  photoIds: ["s123456789"]
});
{
  "reviewId": "r123456789",
  "message": "Review added successfully"
}

Update Review

Updates an existing review. Only the review author can update their own review.
reviewId
Id<'storeReviews'>
required
Review ID to update
rating
number
Updated rating (1-5)
title
string
Updated title
comment
string
Updated comment
photoIds
Id<'_storage'>[]
Updated photo IDs
await convex.mutation(api.stores.reviews.updateReview, {
  reviewId: "r123456789",
  rating: 4,
  comment: "Updated: Still great food, but service was slower this time."
});

Delete Review

Deletes a review and all associated data (helpful votes, flags). Only the review author can delete their own review.
reviewId
Id<'storeReviews'>
required
Review ID to delete
await convex.mutation(api.stores.reviews.deleteReview, {
  reviewId: "r123456789"
});

Get Store Reviews

Lists reviews for a store with pagination, sorting, and filtering. Only visible reviews are returned. Does not require authentication, but authenticated users see their own helpfulness votes.
storeId
Id<'stores'>
required
Store ID
paginationOpts
PaginationOptions
required
Pagination options
sortBy
string
Sort order: newest, oldest, highest_rating, lowest_rating, most_helpful
ratingFilter
number
Filter by specific rating (1-5)
verifiedOnly
boolean
Show only verified purchase reviews
const reviews = await convex.query(api.stores.reviews.getStoreReviews, {
  storeId: "j123456789",
  paginationOpts: { numItems: 10, cursor: null },
  sortBy: "newest",
  verifiedOnly: true
});
{
  "page": [
    {
      "_id": "r123456789",
      "_creationTime": 1700000000000,
      "rating": 5,
      "title": "Excellent food!",
      "comment": "The pizza was amazing.",
      "photoUrls": ["https://..."],
      "isVerifiedPurchase": true,
      "helpfulCount": 3,
      "notHelpfulCount": 0,
      "storeResponse": {
        "comment": "Thank you!",
        "respondedAt": 1700001000000
      },
      "customer": {
        "_id": "c123456789",
        "name": "Jane"
      },
      "userVote": true
    }
  ],
  "isDone": false,
  "continueCursor": "cursor123"
}

Get Store Review Stats

Returns denormalized review statistics for a store. Fast query — reads pre-computed stats from the store document.
storeId
Id<'stores'>
required
Store ID
const stats = await convex.query(api.stores.reviews.getStoreReviewStats, {
  storeId: "j123456789"
});

Get Review

Fetches a single review by ID with full details including customer info, store info, photo URLs, and current user’s permissions and vote.
reviewId
Id<'storeReviews'>
required
Review ID
const review = await convex.query(api.stores.reviews.getReview, {
  reviewId: "r123456789"
});
// response includes: canEdit, canDelete, userVote

Get Customer Reviews

Lists all reviews written by the authenticated customer with pagination. Includes store info and store responses.
paginationOpts
PaginationOptions
required
Pagination options
const myReviews = await convex.query(api.stores.reviews.getCustomerReviews, {
  paginationOpts: { numItems: 10, cursor: null }
});

Add Store Response

Allows a store owner to respond to a review. One response per review.
reviewId
Id<'storeReviews'>
required
Review ID to respond to
comment
string
required
Store response comment
await convex.mutation(api.stores.reviews.addStoreResponse, {
  reviewId: "r123456789",
  comment: "Thank you for your wonderful review! We're glad you enjoyed our pizza."
});

Update Store Response

Allows a store owner to edit their existing response.
reviewId
Id<'storeReviews'>
required
Review ID with the response to update
comment
string
required
Updated response comment
await convex.mutation(api.stores.reviews.updateStoreResponse, {
  reviewId: "r123456789",
  comment: "Updated: Thank you! We've improved our delivery times based on your feedback."
});

Vote on Review

Vote on whether a review is helpful or not. Customers cannot vote on their own reviews. One vote per customer per review (can be changed).
reviewId
Id<'storeReviews'>
required
Review ID to vote on
isHelpful
boolean
required
true for helpful, false for not helpful
await convex.mutation(api.stores.reviews.voteOnReview, {
  reviewId: "r123456789",
  isHelpful: true
});

Flag Review

Report a review for moderation. One flag per customer per review. Creates a pending flag for admin review and sends an admin notification (high priority for offensive/fake reasons).
reviewId
Id<'storeReviews'>
required
Review ID to flag
reason
string
required
Reason for flagging: spam, inappropriate, fake, offensive, irrelevant, other
description
string
Additional details about the flag
await convex.mutation(api.stores.reviews.flagReview, {
  reviewId: "r123456789",
  reason: "fake",
  description: "This customer never ordered from our store"
});
Reviews include photo support, verified purchase indicators, helpfulness voting, store owner responses, and a full flagging/moderation pipeline.