# 08. Feedback API Endpoint for Accuracy Ratings and Storage Consent meta: id: multi-image-user-feedback-08 feature: multi-image-user-feedback priority: P1 depends_on: [multi-image-user-feedback-01] tags: [api, backend, database] objective: - Create a POST endpoint at `/api/feedback` that accepts diagnosis feedback submissions (accuracy rating, notes, image storage consent) and persists them to the database. deliverables: - `src/app/api/feedback/route.ts` — new API route - `src/app/api/feedback/feedback.test.ts` — test file steps: 1. Create `src/app/api/feedback/route.ts`: Route: `POST /api/feedback` Accepts JSON body matching `FeedbackRequest`: ```typescript { sessionId: string; imageIds: string[]; userSpecies?: string; predictedDiseaseId: string; accuracyRating: "correct" | "incorrect" | "unsure"; consentToStoreImages: boolean; userCorrectedSpecies?: string; notes?: string; } ``` Handler logic: - Parse and validate request body - Generate UUID for `id` - Get `modelVersion` from model loader's `getStatus()` - Set `createdAt` to current timestamp - Insert into `diagnosisFeedback` table via Drizzle - Return `FeedbackResponse`: `{ success: true, id: string }` - Handle validation errors with 400 status - Handle DB errors with 500 status Validation rules: - `sessionId` — required, non-empty string - `imageIds` — required, array of non-empty strings, min length 1 - `accuracyRating` — required, must be one of "correct", "incorrect", "unsure" - `consentToStoreImages` — required, boolean - `userSpecies` — optional string - `userCorrectedSpecies` — optional string, only meaningful when accuracy is not "correct" - `notes` — optional string, max 500 characters (with error message if exceeded) 2. Create `src/lib/api/feedback.ts` — client-side helper: - `submitFeedback(data: FeedbackRequest): Promise` - POST to `/api/feedback` with JSON body - 15-second timeout - Handle network errors gracefully 3. Handle edge cases: - Invalid JSON body → 400 with descriptive error - Missing required fields → 400 listing missing fields - Invalid accuracyRating value → 400 with allowed values - Database unreachable → 500 with error message - Duplicate sessionId → allowed (user can submit multiple times for different predictions) 4. CORS and caching: - Add `Cache-Control: no-store` header - No authentication required (public endpoint for feedback) tests: - Unit: valid feedback submission returns 200 with success - Unit: missing required fields return 400 - Unit: invalid accuracyRating returns 400 - Unit: notes over 500 chars returns 400 - Unit: empty imageIds array returns 400 - Unit: client helper `submitFeedback()` makes correct fetch call - Unit: client helper handles network error gracefully - Integration: submit feedback and verify it exists in database acceptance_criteria: - POST /api/feedback accepts valid feedback and stores it - Invalid requests return appropriate 400 errors with descriptive messages - Database stores all fields correctly - Client helper function is usable from any feedback component - Endpoint returns `{ success: true, id }` on success validation: - `npx tsc --noEmit` passes - Unit tests pass: `npx vitest run src/app/api/feedback/` - Manual test: `curl -X POST http://localhost:3000/api/feedback -H 'Content-Type: application/json' -d '{"sessionId":"test","imageIds":["img1"],"predictedDiseaseId":"early-blight","accuracyRating":"correct","consentToStoreImages":false}'` - Verify stored data with direct DB query notes: - No auth needed for MVP — feedback is public and anonymous - imageIds reference images in the uploads directory; no automatic cleanup - A future task could add a review/admin dashboard for browsing feedback entries - Rate limiting could be added later if needed (by sessionId or IP)