Skip to content

tmarktg/feedback-platform

Repository files navigation

Feedback Management Platform

An internal backend service for collecting, classifying, and triaging product feedback across teams and features.

Built with NestJS, GraphQL (code-first), PostgreSQL, and Prisma. Feedback classification runs asynchronously via NestJS's EventEmitter.


Tech Stack

Layer Technology
Framework NestJS (TypeScript)
API GraphQL (code-first via @nestjs/graphql)
ORM Prisma 4
Database PostgreSQL 15
Events @nestjs/event-emitter
Runtime Node.js 16+

Prerequisites

  • Node.js 16+
  • Docker Desktop (for PostgreSQL)

Setup

1. Install dependencies

npm install

2. Start the database

docker compose up -d

3. Run migrations

npm run db:migrate

4. (Optional) Seed sample data

npm run db:seed

5. Start the server

npm run start:dev

The GraphQL endpoint is available at http://localhost:3000/graphql.


Environment Variables

Variable Description
DATABASE_URL PostgreSQL connection string (see .env)
PORT HTTP port (default: 3000)

Data Model

Team ──< User ──< Feedback >──< Feature
                     │
                  Comment
                     │
         FeedbackClassification  (written async by background job)
Entity Key Fields
Team id, name
User id, email, teamId
Feedback id, content, priority (LOW/MEDIUM/HIGH), flagged, submittedBy, assignedTeamId
Feature id, name — M:N with Feedback
Comment id, content, author, feedbackId
FeedbackClassification id, category (BUG/FEATURE_REQUEST/UX_ISSUE/OTHER), confidence, feedbackId

GraphQL API

Queries

# Fetch a single feedback item with all relations
feedback(id: ID!): Feedback!

# List feedbacks with optional filters
feedbacks(
  teamId: ID
  flagged: Boolean
  priority: Priority
  category: FeedbackCategory
): [Feedback!]!

features: [Feature!]!
teams: [Team!]!

teamFeedbackSummary(teamId: ID!): TeamSummary!

Mutations

submitFeedback(
  content: String!
  submittedBy: ID!
  featureIds: [ID!]!
  priority: Priority!
): Feedback!

addComment(feedbackId: ID!, authorId: ID!, content: String!): Comment!

createFeature(name: String!): Feature!
createTeam(name: String!): Team!

Enums & Types

enum Priority         { LOW MEDIUM HIGH }
enum FeedbackCategory { BUG FEATURE_REQUEST UX_ISSUE OTHER }

type TeamSummary {
  totalFeedback: Int!
  flaggedCount:  Int!
  topFeatures:   [Feature!]!
}

Async Classification Flow

When submitFeedback is called:

  1. Feedback is saved to the database and returned immediately.
  2. A FeedbackSubmittedEvent is emitted with the feedback ID and content.
  3. ClassificationListener picks up the event, simulates ~1.5s of async processing, then writes a FeedbackClassification record with a randomly assigned category and confidence score (0.60–0.99).

Classification is available on subsequent queries to feedback(id) or feedbacks(...).


Project Structure

src/
  app.module.ts                         # Root module — wires GraphQL, EventEmitter, Prisma
  main.ts                               # Bootstrap with express.json() middleware
  prisma/
    prisma.module.ts                    # Global Prisma module
    prisma.service.ts                   # PrismaClient with onModuleInit
  common/enums/
    priority.enum.ts
    feedback-category.enum.ts
  modules/
    team/                               # teams, teamFeedbackSummary, createTeam
    feature/                            # features, createFeature
    feedback/                           # feedback/feedbacks queries + submitFeedback
      events/feedback-submitted.event.ts
      listeners/classification.listener.ts
    comment/                            # addComment
    user/                               # User ObjectType (shared)
prisma/
  schema.prisma
  seed.ts
docker-compose.yml

Scripts

Script Description
npm run start Start server
npm run start:dev Start with file watching
npm run build Compile TypeScript
npm run db:migrate Run Prisma migrations
npm run db:seed Insert sample team, user, feature, and feedback

Example Operations

Submit feedback (classification fires in background):

mutation {
  submitFeedback(
    content: "Search results are missing recent items"
    submittedBy: "<user-id>"
    featureIds: ["<feature-id>"]
    priority: MEDIUM
  ) {
    id
    content
    classification { category confidence }
  }
}

Filter flagged, high-priority feedback:

{
  feedbacks(flagged: true, priority: HIGH) {
    id content submittedAt
    submittedBy { email }
    classification { category confidence }
  }
}

Team summary:

{
  teamFeedbackSummary(teamId: "<team-id>") {
    totalFeedback
    flaggedCount
    topFeatures { name }
  }
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors