Reviews Management Controller
Source: internal/api/v1/reviews/controllers/reviews.js
Service: reviews service
Module: Reviews
Overview
The Reviews Management controller provides core CRUD operations for managing customer reviews aggregated from multiple platforms (Google, Facebook, Yelp, etc.). It handles listing reviews with advanced filtering, updating review visibility, and adding internal comments for team collaboration.
Key Capabilities
- Multi-Platform Review Listing with filters, pagination, and search
- Review Visibility Management (hide/show reviews)
- Internal Team Comments on reviews
- Status-Based Filtering with counts per status
- Search Functionality across review content
MongoDB Collections
| Collection | Operations | Purpose |
|---|---|---|
reviews.* | READ, UPDATE | Review data from connected platforms |
review-comments | CREATE | Internal team comments on reviews |
Service Methods
1. getReviews()
Retrieve paginated reviews with filtering, search, and aggregated counts.
Endpoint: GET /reviews
Controller Logic:
const getReviews = catchAsync(async (req, res, next) => {
const query = validate(reviewsSchema.getReviews.query, req.query);
const account_id = req.auth.account_id;
let { filters, page, limit, search } = query;
filters = filters ? { ...filters, page, limit, search } : { page, limit, search };
const { review, paginate, counts, mainStatusCounts } = await reviews.getReviews(
account_id,
filters,
);
res.json({
success: true,
reviews: review,
pagination: paginate,
filter_counts: counts,
mainStatusCounts: mainStatusCounts,
});
});
Request:
GET /api/v1/v1/reviews?page=1&limit=25&search=great&filters[rating]=5&filters[platform]=google
Query Parameters:
page(number) - Page number for paginationlimit(number) - Items per pagesearch(string, optional) - Search term for review contentfilters(object, optional) - Filter criteria:rating- Star rating (1-5)platform- Review platform (google, facebook, yelp, etc.)status- Review status (new, responded, hidden, etc.)date_range- Date filter for review creation
Response:
{
"success": true,
"reviews": [
{
"_id": "review_id",
"account_id": "acc_123",
"platform": "google",
"rating": 5,
"author_name": "John Doe",
"text": "Great service!",
"created_at": "2024-12-01T10:00:00Z",
"status": "new",
"hidden": false,
"response": null
}
],
"pagination": {
"page": 1,
"limit": 25,
"total": 150,
"pages": 6
},
"filter_counts": {
"all": 150,
"5_stars": 120,
"4_stars": 20,
"3_stars": 5,
"2_stars": 3,
"1_star": 2
},
"mainStatusCounts": {
"new": 45,
"responded": 85,
"hidden": 20
}
}
Business Logic:
graph TD
A[Receive Request] --> B[Validate Query Parameters]
B --> C[Extract account_id from Auth]
C --> D[Merge Filters with Pagination]
D --> E[Call reviews.getReviews Service]
E --> F[Service Queries MongoDB]
F --> G[Aggregate Filter Counts]
G --> H[Aggregate Status Counts]
H --> I[Return Reviews + Metadata]
I --> J[Send Response]
Key Features:
- Advanced Filtering: Platform, rating, status, date range
- Full-Text Search: Search across review text and author names
- Aggregated Counts: Returns counts for each filter option
- Status Tracking: Separate counts for new, responded, hidden reviews
- Multi-Tenant Scoped: Auto-filtered by account_id
2. updateReviews()
Update review properties, primarily for hiding/showing reviews from public display.
Endpoint: PUT /reviews/:id
Controller Logic:
const updateReviews = catchAsync(async (req, res, next) => {
const reviewId = req.params.id;
const query = validate(reviewsSchema.updateReview.query, req.query);
const account_id = req.auth.account_id;
let { hide_review } = query;
const reviewData = await reviews.updateReviews({
reviewId,
accountId: account_id,
hideReview: hide_review,
});
res.json({
success: true,
reviews: reviewData,
});
});
Request:
PUT /api/v1/v1/reviews/rev_abc123?hide_review=true
Query Parameters:
hide_review(boolean) - true to hide, false to show
Response:
{
"success": true,
"reviews": {
"_id": "rev_abc123",
"hidden": true,
"updated_at": "2024-12-08T10:00:00Z"
}
}
Business Logic:
graph TD
A[Receive Update Request] --> B[Validate Query Parameters]
B --> C[Extract Review ID from Path]
C --> D[Extract account_id from Auth]
D --> E[Call reviews.updateReviews Service]
E --> F[Verify Review Ownership]
F --> G{Review Belongs to Account?}
G -->|No| H[Throw Not Found Error]
G -->|Yes| I[Update hidden Field]
I --> J[Save to MongoDB]
J --> K[Return Updated Review]
K --> L[Send Response]
Use Cases:
- Hide Negative Reviews: Temporarily hide 1-2 star reviews from widgets
- Show Previously Hidden: Restore hidden reviews after issue resolution
- Manual Moderation: Hide spam or inappropriate reviews
Important Notes:
- ⚠️ Hiding reviews only affects widget display, not platform sync
- ⚠️ Hidden reviews still appear in internal dashboard
- ✅ Multi-tenant security: Can only update own account's reviews
3. comment()
Add internal team comments to reviews for collaboration and notes.
Endpoint: POST /reviews/comment
Controller Logic:
const comment = catchAsync(async (req, res, next) => {
const body = validate(reviewsSchema.comment.body, req.body);
const account_id = req.auth.account_id;
const user_id = req.auth.uid;
await reviews.comment(account_id, user_id, body.id, body.comment);
res.json({
success: true,
message: 'Comment operation successfully done',
});
});
Request:
POST /api/v1/v1/reviews/comment
Content-Type: application/json
{
"id": "rev_abc123",
"comment": "Reached out to customer, issue resolved"
}
Request Body:
{
"id": "rev_abc123",
"comment": "Reached out to customer, issue resolved"
}
Response:
{
"success": true,
"message": "Comment operation successfully done"
}
Business Logic:
graph TD
A[Receive Comment Request] --> B[Validate Request Body]
B --> C[Extract Auth Context]
C --> D{user_id and account_id Valid?}
D -->|No| E[Throw Auth Error]
D -->|Yes| F[Call reviews.comment Service]
F --> G[Verify Review Exists]
G --> H[Create Comment Document]
H --> I[Link to Review + User]
I --> J[Save to review-comments]
J --> K[Update Review metadata]
K --> L[Send Success Response]
Key Features:
- Internal Only: Comments not visible to customers
- User Attribution: Tracks which team member added comment
- Collaboration: Team can discuss review handling strategy
- Audit Trail: Timestamped record of review interactions
Use Cases:
- Document customer outreach efforts
- Note resolution steps for negative reviews
- Flag reviews for management attention
- Track response strategy decisions
Request/Response Flow
Complete Review Management Flow
sequenceDiagram
participant Client
participant Controller
participant Validator
participant Service
participant MongoDB
Client->>Controller: GET /reviews?filters[rating]=5
Controller->>Validator: Validate query schema
Validator-->>Controller: Validated params
Controller->>Service: getReviews(account_id, filters)
Service->>MongoDB: Aggregate reviews collection
MongoDB-->>Service: Reviews + counts
Service-->>Controller: { review, paginate, counts }
Controller-->>Client: JSON response
Client->>Controller: PUT /reviews/:id?hide_review=true
Controller->>Validator: Validate query schema
Validator-->>Controller: Validated params
Controller->>Service: updateReviews({reviewId, hideReview})
Service->>MongoDB: findOneAndUpdate reviews
MongoDB-->>Service: Updated review
Service-->>Controller: Updated review data
Controller-->>Client: Success response
Client->>Controller: POST /reviews/comment
Controller->>Validator: Validate body schema
Validator-->>Controller: Validated body
Controller->>Service: comment(account_id, user_id, id, comment)
Service->>MongoDB: Insert into review-comments
Service->>MongoDB: Update review metadata
MongoDB-->>Service: Success
Service-->>Controller: Operation success
Controller-->>Client: Success message
Edge Cases & Error Handling
Common Scenarios
1. Review Not Found:
// Service throws not found error if review doesn't exist
// or doesn't belong to account_id
throw notFound('Review not found');
2. Invalid Filter Combination:
// Validator catches invalid filter values
// Returns 400 Bad Request with validation errors
3. Search with No Results:
// Returns empty array with valid pagination structure
{
reviews: [],
pagination: { page: 1, limit: 25, total: 0, pages: 0 },
filter_counts: {},
mainStatusCounts: {}
}
4. Multi-Tenant Isolation:
// All queries automatically scope to account_id
// Prevents cross-account data leakage
const filters = { account_id, ...otherFilters };
5. Missing Comment Text:
// Validator requires comment text
// Returns 400 if empty or missing
Authorization
Authentication: Required (JWT Bearer token)
Authorization Rules:
- ✅ Account-scoped: All operations filtered by
account_id - ✅ User context: Comments track
user_idfor attribution - ❌ No cross-account access
- ❌ No public endpoints
Multi-Tenant Pattern:
// Every query includes account_id from auth token
const account_id = req.auth.account_id;
const user_id = req.auth.uid;
// Service layer enforces ownership
await Review.findOne({ _id: reviewId, account_id });
Integration Points
Related Controllers
Dependencies:
- Config Controller: Platform connections required to sync reviews
- Request Review Controller: Reviews generated from review requests
- Widgets Controller: Hidden reviews filtered from widget display
- Auto Response Rules: Review updates trigger auto-response evaluation
Service Integration:
- CRM Module: Link reviews to customer contacts
- Notifications: Alert team when new reviews arrive
- Webhooks: External triggers can create/update reviews
Important Notes
Review Visibility Logic
-
Hidden Reviews:
- Still visible in internal dashboard
- Excluded from public widgets
- Not synced back to platforms
- Can be unhidden anytime
-
Status Tracking:
new: Unread reviewresponded: Has response (manual or auto)hidden: Manually hidden from widgets- Custom statuses possible based on rules
-
Comment System:
- Internal only (never public)
- No edit/delete functionality shown in controller
- Stored in separate collection for audit trail
Performance Considerations
- Pagination: Always use limit to avoid large result sets
- Indexing: account_id, platform, rating should be indexed
- Aggregation: Filter counts use aggregation pipeline (can be slow for large datasets)
- Search: Full-text search may be slow without text index
Data Freshness
- Reviews synced from platforms via background jobs
- Manual updates (hide/comment) immediate
- Widget display may cache review data
- Platform sync typically every 15-60 minutes
Related Documentation
- Config Controller - Platform connection setup
- Widgets Controller - Public review display
- Request Review Controller - Soliciting reviews
- Auto Response Rules - Automated responses
- Reporting - Review analytics
Version: 1.0
Last Updated: December 2024
Status: ✅ Production Ready