Feedback Service
User feedback management service supporting reviews, comments, favorites, feature requests, voting, roadmap, and changelog. Built with NestJS, deployed on AWS Lambda.
Feedback Service (zv-feedback-service)
Feedback management service for the ZoneVast ecosystem. Supports polymorphic reviews (any entity type), comments, favorites, feature requests with voting, public roadmap, and changelog.
Service Info
|| Property | Value |
||----------|-------|
|| Framework | NestJS (TypeScript) |
|| Database | PostgreSQL (feedback schema) |
|| API Prefix | api/v1 |
Base URLs
|| Environment | URL |
||-------------|-----|
|| Test (dev) | https://test.zonevast.com/feedback/api/v1/ |
|| Local | http://localhost:3000/api/v1/ |
|| Lambda | zv-feedback-service-dev |
Required Headers
All authenticated endpoints require:
Authorization: Bearer <access_token>
X-Project-ID: 11
Content-Type: application/json
Multi-tenant: zv-feedback-service resolves the tenant from X-Project-ID first (then JWT projectId, then default 11) for reads and writes — list endpoints, admin stats, roadmap, and changelog are scoped to that project. Use the same header value as the project you selected in the dashboard (zv_project / Developer Platform project switcher).
Seed demo data (local / CI)
From apps/new/developer-platform:
export ACCESS_TOKEN="<JWT>"
# optional: export TARGET_PROJECT_ID=42
npm run seed:feedback
See scripts/seed-feedback.mjs for env vars (FEEDBACK_BASE, PROJECT_GATEWAY, etc.).
Polymorphic Design
The feedback service uses a polymorphic design. Reviews, comments, and favorites can be attached to any entity type:
auction- Auction listingsproduct- Productsorder- Ordersdriver- Driversstore- Storesgroup_deal- Group buying deals
The itemType field determines what entity a review/favorite belongs to.
Reviews
Create a Review
curl -X POST https://test.zonevast.com/feedback/api/v1/reviews \
-H "Authorization: Bearer TOKEN" \
-H "X-Project-ID: 11" \
-H "Content-Type: application/json" \
-d '{
"itemId": 42,
"itemType": "auction",
"rating": 5,
"title": "Great auction!",
"comment": "Smooth experience, fast delivery.",
"images": ["https://file.zonevast.com/img1.jpg"]
}'
Response:
{
"success": true,
"message": "Review created successfully",
"data": {
"id": 1,
"itemId": 42,
"itemType": "auction",
"userId": "uuid-string",
"rating": 5,
"title": "Great auction!",
"comment": "Smooth experience, fast delivery.",
"images": ["https://file.zonevast.com/img1.jpg"],
"status": "approved",
"projectId": 11,
"createdAt": "2026-05-09T00:00:00.000Z",
"updatedAt": "2026-05-09T00:00:00.000Z"
}
}
Create a Guest Review (Email-only)
Guest reviews allow unauthenticated users to submit reviews by providing their email address. Guest reviews are always set to pending status and require admin moderation before becoming visible.
curl -X POST https://test.zonevast.com/feedback/api/v1/reviews \
-H "X-Project-ID: 11" \
-H "Content-Type: application/json" \
-d '{
"itemId": 42,
"itemType": "auction",
"rating": 4,
"title": "Great experience as a guest",
"comment": "I loved the auction process!",
"guestEmail": "visitor@example.com",
"guestName": "John"
}'
No Authorization header is required for guest reviews.
Response:
{
"success": true,
"message": "Guest review submitted successfully and is pending moderation",
"data": {
"id": 2,
"itemId": 42,
"itemType": "auction",
"userId": "guest:visitor@example.com",
"rating": 4,
"title": "Great experience as a guest",
"comment": "I loved the auction process!",
"status": "pending",
"guestEmail": "visitor@example.com",
"guestName": "John",
"isGuest": true,
"source": "email_guest",
"projectId": 11,
"createdAt": "2026-05-16T00:00:00.000Z",
"updatedAt": "2026-05-16T00:00:00.000Z"
}
}
Key differences from authenticated reviews:
- No
Authorizationheader required guestEmailis requiredstatusis alwayspending(requires admin approval)isGuestistruesourceis"email_guest"userIdis auto-generated asguest:{email}
List Reviews
curl "https://test.zonevast.com/feedback/api/v1/reviews?itemType=auction&itemId=42&page=1&limit=20"
Get Review Stats
curl "https://test.zonevast.com/feedback/api/v1/reviews/stats/auction/42"
Response:
{
"success": true,
"data": {
"itemType": "auction",
"itemId": 42,
"averageRating": 4.5,
"totalReviews": 23,
"distribution": { "1": 1, "2": 0, "3": 2, "4": 8, "5": 12 }
}
}
Other Review Endpoints
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /reviews | Public | List reviews (paginated, filterable) |
GET | /reviews/stats/:itemType/:itemId | Public | Rating stats + distribution |
GET | /reviews/:id | Public | Single review detail |
GET | /my-reviews | JWT | Current user's reviews |
PUT | /reviews/:id | JWT | Update own review |
DELETE | /reviews/:id | JWT | Delete own review |
Comments
Create a Comment
curl -X POST https://test.zonevast.com/feedback/api/v1/comments \
-H "Authorization: Bearer TOKEN" \
-H "X-Project-ID: 11" \
-H "Content-Type: application/json" \
-d '{
"entityType": "review",
"entityId": 1,
"content": "I agree, great experience!",
"parentId": null
}'
Reply to a Comment
curl -X POST https://test.zonevast.com/feedback/api/v1/comments \
-H "Authorization: Bearer TOKEN" \
-H "X-Project-ID: 11" \
-H "Content-Type: application/json" \
-d '{
"entityType": "review",
"entityId": 1,
"content": "Thanks for your feedback!",
"parentId": 5
}'
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /comments | JWT | Create comment (supports nested replies via parentId) |
GET | /comments?entityType=review&entityId=1 | Public | List comments |
PUT | /comments/:id | JWT | Update own comment |
DELETE | /comments/:id | JWT | Delete own comment |
Favorites
Toggle Favorite
curl -X POST https://test.zonevast.com/feedback/api/v1/favorites/toggle \
-H "Authorization: Bearer TOKEN" \
-H "X-Project-ID: 11" \
-H "Content-Type: application/json" \
-d '{"itemId": 42, "itemType": "auction"}'
Response:
{
"success": true,
"message": "Added to favorites",
"data": { "isFavorite": true, "favorite": { "id": 1, "itemId": 42, "itemType": "auction" } }
}
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /favorites/toggle | JWT | Toggle favorite (add/remove) |
POST | /favorites | JWT | Add favorite |
GET | /favorites | JWT | List user's favorites |
DELETE | /favorites/:itemId/:itemType | JWT | Remove favorite |
Feature Requests (Posts)
Create a Feature Request
curl -X POST https://test.zonevast.com/feedback/api/v1/posts \
-H "Authorization: Bearer TOKEN" \
-H "X-Project-ID: 11" \
-H "Content-Type: application/json" \
-d '{
"title": "Add dark mode support",
"description": "Please add a dark mode option to the mobile app.",
"category": "feature"
}'
Vote on a Post
curl -X POST https://test.zonevast.com/feedback/api/v1/posts/1/vote \
-H "Authorization: Bearer TOKEN" \
-H "X-Project-ID: 11"
| Method | Endpoint | Auth | Description |
|---|---|---|---|
POST | /posts | JWT | Create post |
GET | /posts?sort=votes&category=feature | Public | List posts (sortable) |
GET | /posts/:id | Public | Single post |
PUT | /posts/:id | JWT | Update own post |
DELETE | /posts/:id | JWT | Delete own post |
POST | /posts/:id/vote | JWT | Upvote |
DELETE | /posts/:id/vote | JWT | Remove vote |
Post statuses: under_review, planned, in_progress, completed, declined
Post categories: bug, feature, improvement, other
Roadmap
curl https://test.zonevast.com/feedback/api/v1/roadmap
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /roadmap | Public | Get roadmap items |
POST | /roadmap | JWT | Create roadmap item |
PUT | /roadmap/:id | JWT | Update roadmap item |
DELETE | /roadmap/:id | JWT | Delete roadmap item |
Changelog
curl https://test.zonevast.com/feedback/api/v1/changelog
| Method | Endpoint | Auth | Description |
|---|---|---|---|
GET | /changelog | Public | Get changelog entries |
POST | /changelog | JWT | Create entry |
PUT | /changelog/:id | JWT | Update entry |
DELETE | /changelog/:id | JWT | Delete entry |
Admin Endpoints
All admin endpoints require JWT authentication.
| Method | Endpoint | Description |
|---|---|---|
GET | /admin/stats | Dashboard stats |
GET | /admin/reviews?status=pending | List reviews for moderation |
PUT | /admin/reviews/:id/status?status=approved | Approve/reject review |
GET | /admin/posts?status=under_review | List posts for review |
PUT | /admin/posts/:id/status?status=planned | Update post status |
Data Models
Review
|| Field | Type | Notes |
||-------|------|-------|
|| id | number | Primary key |
|| itemId | number | Target entity ID |
|| itemType | string | Target entity type |
|| userId | string (UUID) | Review author (UUID for authenticated, guest:{email} for guests) |
|| rating | number | 1-5 |
|| title | string? | Optional title |
|| comment | string? | Review text |
|| images | string[]? | Image URLs |
|| status | string | pending, approved, rejected |
|| guestEmail | string? | Guest email (set for unauthenticated reviews) |
|| guestName | string? | Guest display name |
|| isGuest | boolean | true for guest reviews, false for authenticated |
|| source | string | authenticated or email_guest |
|| projectId | number | Multi-tenant |
FeedbackPost
|| Field | Type | Notes |
||-------|------|-------|
|| id | number | Primary key |
|| title | string | Post title |
|| description | string | Detailed description |
|| category | string | bug, feature, improvement, other |
|| status | string | under_review, planned, in_progress, completed, declined |
|| userId | string (UUID) | Author |
|| votesCount | number | Denormalized vote count |
|| commentsCount | number | Denormalized comment count |
TypeScript Integration
const FEEDBACK_BASE = 'https://test.zonevast.com/feedback/api/v1';
// Create a review
async function createReview(token: string, itemId: number, itemType: string, rating: number, comment: string) {
const res = await fetch(`${FEEDBACK_BASE}/reviews`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'X-Project-ID': '11',
},
body: JSON.stringify({ itemId, itemType, rating, comment }),
});
return res.json();
}
// Get review stats
async function getReviewStats(itemType: string, itemId: number) {
const res = await fetch(`${FEEDBACK_BASE}/reviews/stats/${itemType}/${itemId}`);
return res.json();
}
// Toggle favorite
async function toggleFavorite(token: string, itemId: number, itemType: string) {
const res = await fetch(`${FEEDBACK_BASE}/favorites/toggle`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
'X-Project-ID': '11',
},
body: JSON.stringify({ itemId, itemType }),
});
return res.json();
}
// Vote on a feature request
async function votePost(token: string, postId: number) {
const res = await fetch(`${FEEDBACK_BASE}/posts/${postId}/vote`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'X-Project-ID': '11',
},
});
return res.json();
}
Deployment
cd /home/yousef/Documents/workspace/zonevast/services/zv-feedback-service/
npm run deploy:dev # Deploy to test.zonevast.com
npm run deploy:prod # Deploy to production
What's Next
- Web Integration Guide - How to integrate in Next.js apps
- Mobile Integration Guide - How to integrate in React Native
- Headers & Authentication - Required headers