Sentiment Analysis

Chapter Outline

Chapter 7: Sentiment Analysis

So far, you’ve built:

  • Feedback collection (Airtable, Giscus/Disqus, your own API).
  • Analytics dashboards (summary stats, charts).

But raw comments can still be overwhelming. Instead of manually reading hundreds of comments, we can use sentiment analysis to classify them automatically as:

  • Positive (“This tutorial was great!”)
  • Negative (“I got stuck and couldn’t follow this.”)
  • Neutral (“I tried this on FastAPI version 0.95.”)

In this chapter, we’ll:

  • Learn about sentiment analysis basics.
  • Build sentiment endpoints in FastAPI and NestJS.
  • Grow with a dashboard view that displays aggregated sentiment.

7.1 Sentiment Analysis in Practice

Why it matters:

  • Quickly detect problematic posts (lots of negative feedback).
  • Identify high-impact content (lots of positive feedback).
  • Prioritize improvements without manual triage.

Approaches:

  1. Rule-based (lexicons like VADER in Python).
  2. ML models (pre-trained Hugging Face pipelines).
  3. Custom fine-tuned models (advanced, out of scope here).

7.2 FastAPI Sentiment API

We’ll use Hugging Face transformers for a modern, out-of-the-box solution.

7.2.1 Install Dependencies

We are using a combination of PyTorch with the Hugging Face Transformers library. PyTorch is a deep-learning library, similar to TensorFLow, and Flax.

bash
pip install transformers torch

7.2.2 Sentiment Service

app/sentiment.py
1from fastapi import APIRouter, Depends
2from sqlalchemy.orm import Session
3from transformers import pipeline
4from app.main import get_db
5from app.models import Feedback
6
7router = APIRouter()
8sentiment_model = pipeline("sentiment-analysis")
9
10@router.get("/analytics/sentiment")
11def sentiment_summary(db: Session = Depends(get_db)):
12 comments = db.query(Feedback.comment).filter(Feedback.comment.isnot(None)).all()
13 results = {"positive": 0, "negative": 0, "neutral": 0}
14
15 for (comment,) in comments:
16 if not comment.strip():
17 continue
18 pred = sentiment_model(comment[:512])[0] # truncate to 512 chars
19 label = pred["label"].lower()
20 if "pos" in label:
21 results["positive"] += 1
22 elif "neg" in label:
23 results["negative"] += 1
24 else:
25 results["neutral"] += 1
26 return results

The code above uses a sentiment-analysis task pipeline, using the default model and tokenizer. We are not applying any additional customizations on this pipeline, such as finetuning. We discuss the usage of various HuggingFace Transformer models, their customization, deep-learning frameworks (PyTorch, TensorFlow, etc.), and tools such as LangChain in a separate tutorial series pertaining to developing AI applications.

For now the defaults will suffice.

Register in app/main.py:

python
from app import sentiment
app.include_router(sentiment.router)

7.3 NestJS Sentiment API

For Node.js we’ll use Hugging Face Inference API (instead of running big models locally).

7.3.1 Install Axios

bash
npm install axios

7.3.2 Sentiment Service (Hugging Face API)

src/feedback/feedback.sentiment.service.ts
1import { Injectable } from '@nestjs/common';
2import axios from 'axios';
3import { InjectRepository } from '@nestjs/typeorm';
4import { Repository } from 'typeorm';
5import { Feedback } from './feedback.entity';
6
7@Injectable()
8export class FeedbackSentimentService {
9 constructor(
10 @InjectRepository(Feedback) private repo: Repository<Feedback>,
11 ) {}
12
13 async analyze() {
14 const comments = await this.repo
15 .createQueryBuilder('f')
16 .select('f.comment')
17 .where('f.comment IS NOT NULL')
18 .getRawMany();
19
20 const results = { positive: 0, negative: 0, neutral: 0 };
21
22 for (const row of comments) {
23 if (!row.f_comment?.trim()) continue;
24 const res = await axios.post(
25 'https://api-inference.huggingface.co/models/distilbert-base-uncased-finetuned-sst-2-english',
26 row.f_comment,
27 { headers: { Authorization: `Bearer ${process.env.HF_API_KEY}` } },
28 );
29 const label = res.data[0].label.toLowerCase();
30 if (label.includes('pos')) results.positive++;
31 else if (label.includes('neg')) results.negative++;
32 else results.neutral++;
33 }
34
35 return results;
36 }
37}

7.3.3 Sentiment Controller

src/feedback/feedback.sentiment.controller.ts
1import { Controller, Get } from '@nestjs/common';
2import { FeedbackSentimentService } from './feedback.sentiment.service';
3
4@Controller('analytics')
5export class FeedbackSentimentController {
6 constructor(private readonly service: FeedbackSentimentService) {}
7
8 @Get('sentiment')
9 analyze() {
10 return this.service.analyze();
11 }
12}

7.4 Sentiment Dashboard

We’ll extend our React dashboard (from Chapter 6) to visualize sentiment.

src/components/SentimentChart.tsx
1import React, { useEffect, useState } from 'react';
2import { Pie } from 'react-chartjs-2';
3
4export const SentimentChart: React.FC<{ apiUrl: string }> = ({ apiUrl }) => {
5 const [data, setData] = useState<any>(null);
6
7 useEffect(() => {
8 fetch(`${apiUrl}/analytics/sentiment`).then(res => res.json()).then(setData);
9 }, [apiUrl]);
10
11 if (!data) return <p>Loading sentiment...</p>;
12
13 return (
14 <div>
15 <h3>Comment Sentiment</h3>
16 <Pie
17 data={{
18 labels: ['Positive', 'Negative', 'Neutral'],
19 datasets: [
20 {
21 data: [data.positive, data.negative, data.neutral],
22 backgroundColor: ['#36A2EB', '#FF6384', '#FFCE56'],
23 },
24 ],
25 }}
26 />
27 </div>
28 );
29};

Usage in AnalyticsDashboad:

tsx
<SentimentChart apiUrl={apiUrl} />

The following is a demonstration of the updated analytics dashboard.

Analytics Dashboard with Sentiment Analysis Summary

7.5 Use Case Example

  • A blog post on “Python Metaclasses” gets 30 comments.
  • Sentiment analysis shows 20 negative comments (“hard to follow,” “too advanced”).
  • You now know this post needs rewriting or splitting into beginner + advanced versions.

7.6 Summary

In this chapter, you:

  • Learned why sentiment analysis matters for feedback.
  • Built sentiment endpoints in FastAPI and NestJS.
  • Extended the React dashboard with a sentiment chart.

Now you’re not just collecting feedback — you’re extracting meaning from it.

7.7 Exercise

  1. Add a /analytics/sentiment endpoint in your backend.
  2. Integrate the SentimentChart into your dashboard.
  3. Submit a few test comments:
    • “This was fantastic!” → should be Positive.
    • “I couldn’t understand anything.” → should be Negative.
    • “Installed FastAPI 0.97 today.” → likely Neutral.
  4. Confirm the chart updates correctly.

7.8 Next Step

In the final chapter, we’ll reflect on the journey:

  • From prototype → hybrid → full-stack → analytics + ML.
  • Lessons learned about tradeoffs.
  • How feedback systems tie back to the idea of “writing as learning.”

Feedback