medusa-review-rating
v0.0.38
Published
A starter for Medusa plugins.
Maintainers
Readme
medusa-review-rating
Medusa v2 plugin that adds customer product reviews, rating aggregation, moderation APIs, and admin product review management UI.
Plugin Overview
medusa-review-rating provides a full review/rating layer for Medusa storefronts and admin operations:
- Customers can create and manage product reviews.
- Admins can list, moderate (approve/reject), and delete reviews.
- Product rating aggregates are recalculated from approved reviews and stored on
product(total_rating_count,total_rating_sum). - Includes workflows and workflow steps for create/update/status/delete review flows.
- Includes a Medusa Admin widget for product-level review management.
It is built for Medusa v2 (uses @medusajs/framework, module architecture, workflows SDK, and admin SDK).
Installation & Setup
1) Install package
npm install medusa-review-rating
# or
yarn add medusa-review-rating2) Register plugin/module in medusa-config.ts
import { defineConfig } from "@medusajs/framework/utils"
export default defineConfig({
modules: {
reviews: {
resolve: "medusa-review-rating",
options: {
auto_approve: true,
verify_purchase: false,
multiple_rating: false,
},
},
},
plugins: [
{
resolve: "medusa-review-rating",
},
],
})3) Run migrations
npx medusa db:migrateMigration creates:
reviewtable- product columns:
total_rating_count,total_rating_sum - indexes for review and product rating fields
Configuration (config.ts / plugin options)
This plugin does not use a separate config.ts; options are resolved inside src/modules/reviews/service.ts.
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
| auto_approve | boolean | Optional | true | If true, newly submitted reviews are immediately approved; otherwise created as pending. |
| verify_purchase | boolean | Optional | false | If true, customer must have purchased product (completed order) to submit review. |
| multiple_rating | boolean | Optional | false | If false, one review per customer/product; if true, allow multiple. |
| multipleRating | boolean | Optional | false | CamelCase alias accepted and normalized to multiple_rating. |
Complete example config
{
modules: {
reviews: {
resolve: "medusa-review-rating",
options: {
auto_approve: false,
verify_purchase: true,
multiple_rating: true,
},
},
},
plugins: [{ resolve: "medusa-review-rating" }],
}Environment Variables
No runtime process.env.* reads were found in src plugin code.
| Variable | Used in runtime code | Notes | |---|---|---| | None | No | Configuration is driven by module options, not env lookup. |
⚠️ Note:
src/modules/README.mdcontains a genericprocess.env.API_KEYdocumentation example, but it is not runtime logic.
REST APIs / Routes
Store APIs
POST /store/reviews
- Auth: Customer JWT required
- Body schema:
| Field | Type | Required |
|---|---|---|
| product_id | string | Yes |
| rating | number (1..5) | Yes |
| title | string | No |
| description | string | No |
| images | string[] | No |
- Response: workflow result containing created
review - Notes:
- Returns
409when duplicate review is blocked (multiple_rating = false) - Enforces purchase verification when
verify_purchase = true
- Returns
GET /store/reviews/:id
- Auth: optional
- Access behavior:
- owner can view own review in any status
- non-owner can only view approved reviews
- Response:
{ review }
PUT /store/reviews/:id
- Auth: Customer JWT required
- Body schema:
| Field | Type | Required |
|---|---|---|
| title | string | No |
| description | string | No |
| rating | number (1..5) | No |
- Validation: at least one field required
- Response: workflow result with updated
review
GET /store/reviews/me
- Auth: Customer JWT required
- Query params:
| Field | Type | Required |
|---|---|---|
| product_id | string | No |
| status | pending \| approved \| rejected | No |
- Response:
reviewslistsummarywithtotal_reviews,average_rating,total_rating_sum
GET /store/products/:id/reviews
- Auth: optional
- Behavior:
- unauthenticated: approved reviews only
- authenticated: own reviews (all statuses) + others’ approved reviews
- Response:
{ reviews }
GET /store/products/:id/reviews/me
- Auth: Customer JWT required
- Response:
reviewsfor customer+productsummaryfor that product/customer
GET /store/ratings
- Auth requirement: follows Store API access; handler expects
AuthenticatedMedusaRequest - Query params:
| Field | Type | Required | Description |
|---|---|---|---|
| product_id | string or comma-separated list | No | Filter product ids |
| limit | integer string | No | Max rows |
| offset | integer string | No | Pagination offset |
- Response:
{ rating: [{ product_id, average_rating, total_reviews, total_rating_sum }] }
GET /store/plugin
- Health endpoint, returns HTTP
200.
Admin APIs
GET /admin/reviews
- Auth: Admin JWT (admin namespace route)
- Query: passed directly to
listReviews - Response:
{ reviews } - Note: current implementation uses fixed
skip: 0,take: 20in options.
POST /admin/reviews/:id
- Auth: Admin JWT
- Body schema:
| Field | Type | Required |
|---|---|---|
| status | pending \| approved \| rejected | Yes |
- Response: workflow result with updated
review - Triggers conditional product rating recalculation.
DELETE /admin/reviews/:id
- Auth: Admin JWT
- Response:
{ id, object: "review", deleted: true } - Triggers product rating recalculation.
GET /admin/plugin
- Health endpoint, returns HTTP
200.
Important endpoint examples
# Create review (customer)
curl -X POST "http://localhost:9000/store/reviews" \
-H "Content-Type: application/json" \
-H "x-publishable-api-key: <PUBLISHABLE_KEY>" \
-H "Authorization: Bearer <CUSTOMER_JWT>" \
-d '{
"product_id": "prod_123",
"rating": 5,
"title": "Great product",
"description": "Highly recommended"
}'# List product reviews
curl -X GET "http://localhost:9000/store/products/prod_123/reviews" \
-H "x-publishable-api-key: <PUBLISHABLE_KEY>"# Get ratings summary
curl -X GET "http://localhost:9000/store/ratings?product_id=prod_123,prod_456&limit=20" \
-H "x-publishable-api-key: <PUBLISHABLE_KEY>"# Admin approve review
curl -X POST "http://localhost:9000/admin/reviews/rev_123" \
-H "Authorization: Bearer <ADMIN_JWT>" \
-H "Content-Type: application/json" \
-d '{ "status": "approved" }'Services
ReviewModuleService (src/modules/reviews/service.ts)
Manages review persistence, rating calculation, and purchase verification.
Key methods:
getOptions()- Returns resolved module options.
calculateProductRating(productId: string)- Recomputes approved-review aggregates and updates product rating columns.
verifyPurchase(customerId: string, productId: string)- Checks completed customer orders joined to line items for product purchase evidence.
Also inherits CRUD methods from MedusaService for review entity operations (listReviews, retrieveReview, createReviews, updateReviews, deleteReviews).
Workflows & Steps (Medusa v2)
Workflows
create-review
- Input:
customer_id,product_id,rating, optionaltitle,description,images,allow_multiple_reviews - Steps:
get-review-optionsvalidate-single-reviewverify-purchasecreate-reviewcalculate-product-rating(only when auto-approved)
- Output:
{ review }
update-review
- Input:
id, partial review data (title,description,rating) - Steps:
retrieve-reviewupdate-reviewcalculate-product-rating
- Output:
{ review }
update-review-status
- Input:
id,status - Steps:
retrieve-reviewupdate-reviewdetermine-rating-recalculationcalculate-product-rating
- Output:
{ review }
delete-review
- Input:
id - Steps:
retrieve-reviewdelete-reviewcalculate-product-rating
- Output:
{ id, object: "review", deleted: true }
Step summary
get-review-options: resolves service/module options.validate-single-review: blocks duplicate product review when multiple disallowed.verify-purchase: enforces optional purchase requirement.create-review: persists review with status derived from auto-approve.retrieve-review: loads review by id.update-review: applies update and returns latest row.delete-review: deletes review by id.determine-rating-recalculation: status-transition-based recalc decision.calculate-product-rating: recalculates/updates product rating metrics.
Subscribers / Event Hooks
No subscribers are defined in this plugin (src/subscribers contains README only).
Admin UI / Widgets
ProductReviewsWidget
- Placement:
product.details.after - File:
src/admin/widgets/product-reviews-widget.tsx - Renders:
- rating summary for approved reviews
- filter controls (
all,pending,approved,rejected) - review list with metadata, stars, images, and moderation controls
- User interactions:
- approve/reject/restore review
- delete review
- status filtering
- Data consumed:
data.idproduct id from admin widget context/admin/reviewslist API/admin/reviews/:idstatus update/delete APIs
Models & Entities
review model
| Field | Type | Nullable |
|---|---|---|
| id | id | No |
| product_id | text | No in model intent (migration allows null) |
| customer_id | text | No in model intent (migration allows null) |
| rating | number | No |
| title | text | Yes |
| description | text | Yes |
| images | json | Yes |
| verified_purchase | boolean | No |
| status | enum(pending,approved,rejected) | No |
Related core Medusa data:
- Stores
product_id(links to product table by id). - Stores
customer_id(links to customer record by id). - Rating aggregates written to
product.total_rating_countandproduct.total_rating_sum.
Use Cases & Examples
Verified buyers-only reviews
- Enable
verify_purchase: trueand usePOST /store/reviewsfor trusted feedback.
- Enable
Moderated review publishing
- Set
auto_approve: false; admins review queue via widget or/admin/reviews+/admin/reviews/:id.
- Set
Single review per customer/product
- Keep
multiple_rating: false; duplicate submission returns409.
- Keep
Public storefront ratings summary
- Use
GET /store/ratingsto render rating cards for product lists.
- Use
Customer review history
- Use
GET /store/reviews/meandGET /store/products/:id/reviews/meto show account-level and product-level review history.
- Use
Troubleshooting
401 Unauthorized on store review routes
- Cause: missing customer auth for protected endpoints (
/store/reviews,/store/reviews/me,/store/products/:id/reviews/me). - Fix: include customer JWT (or session cookie) and publishable API key where applicable.
409 You have already reviewed this product...
- Cause: duplicate review with
multiple_ratingdisabled. - Fix: enable
multiple_rating: trueor update existing review viaPUT /store/reviews/:id.
You must purchase the product before submitting a review
- Cause:
verify_purchaseenabled and no completed order match. - Fix: disable verify flag or ensure customer has completed order with that product.
Product rating not updating as expected
- Cause: only approved reviews affect rating calculation.
- Fix: approve reviews (
POST /admin/reviews/:id) or ensure workflow recalculation step runs for the transition.
Database connection not available
- Cause: underlying manager/knex unavailable in service calls.
- Fix: verify module registration and Medusa DB bootstrap; rerun server and check plugin loading.
Missing review table or rating columns
- Cause: migration not executed.
- Fix: run
npx medusa db:migrate.
