A modern, full-stack news aggregator built with Next.js 16, Tailwind CSS, shadcn/ui, Prisma, and SQLite. Aggregates articles from BBC, The Guardian, TechCrunch, Wired, Ars Technica, Hacker News, Reddit, AP News, and optionally NewsAPI.
- Multi-source aggregation — RSS feeds (no key needed) + NewsAPI + Hacker News + Reddit
- 7 categories — World, Tech, Business, Sports, Science, Entertainment, Politics
- Deduplication — Cosine similarity + Levenshtein distance groups similar stories; cards show "N sources" count
- Relevance scoring — Recency × source-count composite score ranks the feed
- Trending Now — Sidebar highlights stories covered by 2+ sources
- Bookmarks — Save articles to SQLite; persisted across sessions
- Search — Full-text search with category, source, and date-range filters
- Infinite scroll — Client-side pagination with IntersectionObserver
- Dark / Light mode — System-preference aware, toggle in navbar
- Mobile-first — Responsive grid, horizontal-scroll category chips, slide-out nav drawer
- Rate limiting — 60 req/min per IP on all API routes
- SEO — Open Graph + meta tags on every page
- Caching — Articles cached in SQLite; stale articles (>7 days, unbookmarked) auto-purged
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router) |
| Styling | Tailwind CSS v4 + shadcn/ui |
| Database | SQLite via Prisma 7 + better-sqlite3 |
| RSS parsing | rss-parser |
| Theme | next-themes |
npm installcp .env.local .env.local # already exists — just open and editOpen .env.local. All keys are optional — the app works out-of-the-box with free RSS feeds:
# Optional — 100 req/day free tier at newsapi.org/register
NEWSAPI_KEY=your_key_here
# Optional — free read-only at reddit.com/prefs/apps (choose "script" type)
REDDIT_CLIENT_ID=your_client_id
REDDIT_CLIENT_SECRET=your_secretnpx prisma migrate devnpm run devOpen http://localhost:3000.
Click the refresh icon (↻) in the navbar, or visit POST /api/fetch to trigger a fetch from all sources. Articles are cached in SQLite and auto-refresh every 15 minutes.
src/
├── app/
│ ├── api/
│ │ ├── articles/ # GET paginated feed
│ │ ├── bookmarks/ # GET / POST / DELETE bookmarks
│ │ ├── fetch/ # GET (auto) / POST (force) news fetch
│ │ ├── search/ # GET full-text search
│ │ ├── sources/ # GET source list with counts
│ │ └── trending/ # GET trending stories
│ ├── bookmarks/ # Saved articles page
│ ├── category/[slug]/ # Per-category feed
│ ├── search/ # Search results page
│ └── page.tsx # Home feed
├── components/
│ ├── layout/ # Navbar, FilterChips, SearchBar, ThemeProvider
│ └── news/ # ArticleCard, ArticleGrid, TrendingSection, etc.
├── lib/
│ ├── adapters/ # Source adapters (NewsAPI, RSS, HackerNews, Reddit)
│ ├── dedup.ts # Cosine similarity deduplication
│ ├── fetcher.ts # Orchestrates all adapters + DB storage
│ ├── prisma.ts # Prisma client singleton
│ └── rate-limit.ts # In-memory rate limiter
└── types/
└── article.ts # Shared TypeScript types + category config
| Method | Endpoint | Description |
|---|---|---|
GET |
/api/articles |
Paginated feed. Params: category, source, page, pageSize, sortBy |
GET |
/api/search |
Search. Params: q, category, source, from, to, page |
GET |
/api/trending |
Top stories by source count. Param: limit |
GET |
/api/bookmarks |
List bookmarks |
POST |
/api/bookmarks |
Save bookmark { articleId } |
DELETE |
/api/bookmarks?articleId= |
Remove bookmark |
GET |
/api/fetch |
Fetch if >15 min since last fetch |
POST |
/api/fetch |
Force fetch from all sources |
GET |
/api/sources |
List sources with article counts |
| Source | Categories |
|---|---|
| BBC News | World, Tech, Business, Science, Sports |
| The Guardian | World, Tech, Politics, Science |
| TechCrunch | Tech, Startups |
| Wired | Tech |
| Ars Technica | Tech |
| AP News | World, Business, Sports, Science, Politics |
- Create
src/lib/adapters/mysource.tsextendingBaseAdapter - Implement
fetch()returningArticle[] - Register it in
src/lib/fetcher.tsadaptersarray