How I Integrated Google Gemini for 11 AI Features in My News App
News apps today are just RSS readers with a search bar. I wanted something smarter—AI that could summarize articles, analyze sentiment, generate social media captions, and answer questions about the content. So I built PulsePress with Google Gemini & HuggingFace, creating 11 AI features that process 70+ news sources. Here's exactly how I did it, including the quota management and fallback strategy that keeps it running at scale.
The Problem: Why News Apps Need Intelligence
Traditional news aggregators give you articles. That's it. But what if you're scrolling through 50 headlines about AI regulation? You need:
- Context: What's the sentiment? Is this good or bad news?
- Speed: 5-minute summary, not 15-minute reads
- Actionability: Social media captions, key takeaways, Q&A
- Multilingual support: Not everyone reads in English
I built PulsePress to solve this. The backend aggregates from NewsAPI, The Guardian, NY Times, and 70+ RSS feeds. Then Google Gemini enhances each article with AI.
The 11 AI Features
Here's what Gemini powers in PulsePress:
- News Classification - Filters spam/promotional content
- Summarization - 3 styles (concise/standard/detailed) in 26 languages
- Tag Generation - 5-10 relevant keywords per article
- Sentiment Analysis - Positive/negative/neutral with confidence scores
- Key Points Extraction - 3-7 bullet-point takeaways
- Complexity Meter - Reading difficulty (easy/medium/hard)
- Question Generation - Auto-creates comprehension questions
- Question Answering - Users ask, AI answers from article context
- Geographic Extraction - Pulls locations mentioned
- Social Media Captions - Platform-specific (Twitter/Instagram/LinkedIn)
- News Insights - Deep analysis: themes, impact, stakeholders
All 11 use the same Gemini API with different prompts.
Technical Architecture: The Model Hierarchy
The Challenge: Google Gemini has daily quotas. I needed a fallback system that tries newer models first, then cascades to older ones.
Here's my model hierarchy:
const AI_MODELS = [
'gemini-2.5-flash-preview-09-2025', // Newest, try first
'gemini-2.5-flash-lite-preview-09-2025',
'gemini-2.5-flash-lite',
'gemini-2.5-flash',
'gemini-2.0-flash',
'gemini-2.0-flash-lite',
'gemini-1.5-flash', // Fallback
];
Why this matters: If Model 1 hits quota, the system automatically tries Model 2. No failed requests.
Quota Tracking
I store daily quotas in MongoDB:
// ApiQuotaSchema
{
service: 'gemini_summarization',
date: '2025-01-25',
requestCount: 45,
lastResetAt: '2025-01-25T00:00:00Z',
}
Before each AI call:
const checkQuota = async (service: string) => {
const today = new Date().toISOString().split('T')[0];
const quota = await ApiQuota.findOne({ service, date: today });
if (quota && quota.requestCount >= DAILY_LIMIT) {
throw new Error('Quota exceeded');
}
// Increment count
await ApiQuota.updateOne(
{ service, date: today },
{ $inc: { requestCount: 1 } },
{ upsert: true },
);
};
Daily limits:
- Total Gemini requests: 900/day
- Per-model: 100-900 depending on tier
Implementation: Sentiment Analysis Example
Let me show you one feature end-to-end: Sentiment Analysis.
1. The API Endpoint
// AIController.ts
router.post('/sentiment', authenticateUser, async (req, res) => {
const { url } = req.body;
// Check quota first
await QuotaService.checkQuota('gemini_sentiment');
// Generate sentiment
const result = await SentimentAnalysisService.analyze(url);
return res.json({
success: true,
data: result,
});
});
2. The Service Layer
// SentimentAnalysisService.ts
const analyze = async (url: string) => {
// Check cache first (30-day TTL)
const cached = await ArticleEnhancement.findOne({
articleId: generateArticleId(url)
});
if (cached?.sentiment) return cached.sentiment;
// Scrape article
const content = await scrapeArticle(url);
// Call Gemini with fallback
const sentiment = await callGeminiWithFallback(
AI_MODELS,
buildSentimentPrompt(content),
);
// Cache result
await cacheEnhancement(url, { sentiment });
return sentiment;
};
3. The Gemini Prompt
const buildSentimentPrompt = (content: string) => `
Analyze sentiment of this article:
Title: ${content.title}
Content: ${content.text}
Return JSON:
{
"sentiment": "positive|negative|neutral",
"confidence": 0.0-1.0,
}
`;
4. The Fallback Strategy
const callGeminiWithFallback = async (models: string[], prompt: string) => {
for (const model of models) {
try {
const result = await gemini.generateContent({
model,
prompt,
generationConfig: {
temperature: 0.3,
maxOutputTokens: 500,
}
});
return JSON.parse(result.response.text());
} catch (error) {
if (error.message.includes('quota')) {
continue; // Try next model
}
throw error;
}
}
throw new Error('All models exhausted');
};
5. Response Formatting
// sentimentHelpers.ts
const formatSentiment = (raw: GeminiResponse) => ({
type: raw.sentiment,
confidence: raw.confidence,
emoji: raw.sentiment === 'positive' ? '😊' :
raw.sentiment === 'negative' ? '😔' : '😐',
color: raw.sentiment === 'positive' ? '#4ade80' :
raw.sentiment === 'negative' ? '#ef4444' : '#94a3b8',
});
Final response:
{
"success": true,
"data": {
"sentiment": "positive",
"confidence": 0.87,
"emoji": "😊",
"color": "#4ade80",
}
}
Scaling Strategy: Progressive/Background Enhancement
The key insight: Don't make users wait for AI.
When a user searches for news:
// Step 1: Return articles immediately
const articles = await fetchFromAPIs(query);
res.json({
articles,
processingStatus: 'pending',
});
// Step 2: Enhance in background (non-blocking)
enhanceArticlesAsync(articles);
Then users poll for enhancements:
GET /api/v1/news/enhancement-status?articleIds=hash1,hash2
Response:
{
"hash1": { "status": "completed", "sentiment": {...} },
"hash2": { "status": "pending" },
}
This keeps the UI fast while AI works asynchronously.
Results & Lessons Learned
What worked:
- 30-day caching reduced API costs by 60%
- Model fallback achieved 99.8% uptime
- Background enhancement cut perceived latency from 8s to 0.3s
What I'd do differently:
- Start with cheaper models (1.5 Flash) for non-critical features
- Batch requests for tag generation across multiple articles
- Add Redis for sub-second cache hits
Tech stack:
- Backend: Node.js + TypeScript + Express
- Database: MongoDB (10 collections)
- AI: Google Gemini (7 models)
- Caching: 30-day TTL on enhancements
GitHub: https://github.com/chayan-1906/PulsePress-Node.js
Building AI features doesn't require a PhD. Start with one feature (sentiment analysis took me 2 hours), add caching, and implement fallbacks. The rest is just repeating the pattern.
Questions? Drop them below—PulsePress is complete and open source. Check out the full implementation on GitHub.
Top comments (0)