Interactive Comment Section

React (Vite), Tailwind CSS, Node.js, Express.js, PostgreSQL (via Prisma ORM), Google Perspective API, Docker + Docker Compose

This project was a personal portfolio project designed to simulate a professional-grade interactive comments feature, the kind you'd see on blog platforms, forums, or product reviews. I built it from the ground up to demonstrate my ability to architect a scalable full-stack app, focusing on clean UI/UX, structured state management, API development, form validation, and backend moderation using AI sentiment analysis.

All work was done by me, from frontend to backend, database schema, and deployment on Render.

Problem Statement

Problem Statement

Most comment systems today lack real-time moderation, structured replies, or safe UX practices. This project solves that by creating a fully interactive, nested commenting system with built-in sentiment analysis, toxicity flagging, and modular voting, editing, and reply logic. The goal was to show not just technical execution, but strong product thinking, where user-generated content needs structure, empathy, and resilience.

Technical Architecture

Technical Architecture

  1. Vite + React
    I chose Vite to scaffold the frontend because of its lightning-fast development server, native ESM support, and minimal configuration overhead. For the UI layer, React felt like the natural fit given its reusable component model and my prior familiarity. I intentionally avoided Next.js since server-side rendering wasn’t necessary; the app was fully client-rendered and paired with a separate backend.

  2. Node.js + Express
    On the backend, I used Node.js with Express to keep things lightweight and flexible. Express allowed me to quickly spin up custom API routes and easily integrate external services like the Perspective API. I briefly looked into Fastify for performance benefits, but Express had a lower barrier to entry and broader ecosystem support for the type of app I was building.

  3. Prisma ORM + PostgreSQL
    For data modeling, I used Prisma with PostgreSQL. Prisma gave me type-safe database queries and a seamless development workflow, while PostgreSQL offered solid relational data handling—crucial for managing nested comment threads. I considered Sequelize and TypeORM but found Prisma much easier to work with, especially with its intuitive schema syntax and strong TypeScript integration.

  4. Google Perspective API
    To flag harmful or toxic comments, I integrated Google’s Perspective API. It let me offload the complexity of NLP and content moderation while still delivering AI-level analysis. I did consider open-source sentiment models but ruled them out to avoid the overhead of training, maintaining, or hosting them myself.

  5. Docker + Docker Compose
    I containerized both the backend server and database using Docker, and used Docker Compose to manage them together during development. This gave me a consistent local environment and simplified deployment by eliminating “it works on my machine” bugs. I appreciated being able to version-control the full infrastructure setup.

  6. Render
    For hosting, I chose Render because it let me deploy the frontend, backend, and PostgreSQL database from one dashboard. It handled my GitHub auto-deploys, managed my environment variables, and provided persistent storage for the database; all without needing to configure a CI/CD pipeline from scratch. I briefly compared Heroku and Railway, but Render offered better database performance and a more modern UI for managing services.

  7. Zod
    I used Zod to handle backend form validation. Its schema-first design fit naturally into my workflow and made error handling cleaner when validating comment inputs. I initially considered Joi, but I preferred Zod’s syntax, native TypeScript support, and how well it integrated with the rest of my stack.

Core Features

Core Features

  1. Nested Replies
    Users can reply to any comment, and replies are automatically nested beneath their parent. Each comment maintains full context and hierarchy, improving readability and flow in discussions.

  1. Comment Posting with Moderation
    Users can post comments in real time. Before any comment is saved, it passes through Google’s Perspective API to check for toxicity. Harmful content is rejected with a clear, user-facing error, no moderation team needed.

  1. Inline Editing & Deletion
    Users can edit or delete their own comments. Comments that already have replies cannot be edited to preserve conversation integrity. I built a custom UX state to keep the form open when errors occur (e.g., failed validation or flagged toxicity).

  1. Voting System
    Each comment supports upvotes and downvotes. Users can increment or decrement votes to signal agreement or disagreement; vote counts are stored and updated via backend routes using Prisma.

  1. UX-Safe Form Handling
    Every form (comment, reply, edit) is fully validated both on the frontend and backend. If an error occurs, forms stay open with helpful inline messages. This prevents user confusion and maintains flow.


  2. Error Handling & Modals
    Custom modals handle confirmation for deletions and global error messages. State is managed via hooks and conditionally rendered overlays, ensuring a clean, non-jarring UI.

Frontend Folder Structure

I organized the frontend using a modified atomic design structure to keep components modular and scalable. Reusable base components like buttons, overlays, and modals lived inside a shared /components folder, grouped by function (e.g., overlay, icons). More complex, context-specific components like comments and forms were separated into a /pages/comments directory. This helped me isolate logic, avoid prop drilling, and reuse elements like the delete modal and voting controls across parent and nested comment threads. I kept styles mostly Tailwind-based for consistency and speed, relying on utility classes rather than custom stylesheets.

API Route Strategy

On the backend, I used a clean RESTful API design with Express. Each route was defined under /api/comment, handling different concerns like fetching, creating, editing, deleting, and voting. I separated POST, PATCH, PUT, and DELETE routes by HTTP verb and endpoint for clarity. Sentiment analysis was integrated into the POST and PUT routes, so any new or updated comment was automatically screened before being saved. I kept all route logic in a single comment.routes.ts file to maintain visibility across operations during this MVP stage but modularized utility functions like analyzeComment and validation schemas for cleaner logic separation.