✦ Inspiration
We live in the age of information overload. Every news app floods you with whatever gets the most clicks — not what you actually want to read. I wanted to build something different: a platform where you define your feed from the very first login, and the content actually reflects your curiosity.
That's how FocusFeed was born — a clean, fast, full-stack personalized news experience built around your interests, not an algorithm's agenda.
🔨 How I Built It
FocusFeed is a full-stack web application with a clean separation of concerns across every layer.
Backend — Node.js + Express REST API
The server is structured around the MVC pattern:
- Models (Mongoose):
User,Bookmark,Category— each with validation, indexing, and pre-save hooks - Controllers: Separate controllers for auth, articles, bookmarks, users, and admin operations
- Services: A dedicated
news.service.jshandles all external API communication with a multi-provider fallback chain (NewsAPI → GNews → MediaStack → TheNewsAPI), andanalytics.service.jsruns MongoDB aggregation pipelines for the admin dashboard - Middleware:
auth.middleware.jsvalidates JWT on every protected route;role.middleware.jsenforces admin-only access usingrestrictTo() - Utils: Standardized
sendSuccess/sendErrorresponse helpers and JWTgenerateToken/verifyTokenwrappers keep the codebase consistent
Authentication uses bcryptjs (12 salt rounds) for password hashing and
7-day JWT tokens stored in sessionStorage for XSS safety.
Frontend — Vanilla HTML, CSS & JavaScript
No frontend frameworks — every page is hand-crafted:
- A custom CSS design system built on CSS variables supports full light/dark theming, consistent spacing, shadows, and radius tokens across all 12+ pages
- Responsive layouts using CSS Grid and Flexbox — mobile bottom nav, collapsible sidebar, adaptive feed grid
- A shared API layer (
api.js) centralizes allfetchcalls with Bearer token injection, 401 auto-redirect, and session cleanup - Session management without a framework: token validation on every page
load, a 30-minute inactivity auto-logout with a 2-minute warning toast, and
cross-tab last-activity tracking via
localStorage
News Aggregation
Articles are fetched live by interest category. The feed is built by querying multiple news APIs in sequence — if one provider fails or hits a rate limit, the next one takes over automatically. 18 interest categories are supported: Technology, Science, Health, Business, Sports, Entertainment, Politics, Education, Environment, World, Finance, Travel, Religion, Food, Gaming, Music, Art, and Fashion.
Admin Panel
A full admin control center with:
- Real-time platform metrics (total users, active users, new signups this week, total bookmarks)
- Top interest analytics using a MongoDB
$unwind+$groupaggregation:
$$ \text{Interest Score}i = \sum{u \in \text{Users}} \mathbb{1}[\text{interest}_i \in u.\text{interests}] $$
- User management table with live activate/deactivate toggles
- Recent signups report
🧠 What I Learned
- How to design a layered Express architecture — routes → middleware → controllers → services → models — that stays clean as the project grows
- Building a multi-API fallback system for resilient news fetching
- Creating a complete custom design system in vanilla CSS with full theming, without any UI library
- Implementing JWT authentication end-to-end: token generation, route protection, role-based guards, and graceful expiry handling
- Writing MongoDB aggregation pipelines for real analytics
- Managing complex client-side state across multiple pages without React or any state management library
⚡ Challenges I Faced
News API rate limits were the hardest obstacle. NewsAPI's free tier cuts
off quickly under real usage, so I designed a fallback chain across four
providers. Getting consistent article shapes out of four different API
response formats required a normalization layer in news.service.js.
Session state without a framework was deceptively complex. Keeping the
user object consistent across 12+ pages, syncing theme preferences, handling
token expiry mid-session, and running the inactivity timer — all without
React context or a store — required careful design of the shared api.js
layer that every page loads first.
Admin access security needed to be enforced at two levels: the frontend
validates an access code before the form even submits, and the backend
restrictTo('admin') middleware independently verifies the JWT role claim —
so neither layer alone is the single point of failure.
Responsive design from scratch — building a collapsible sidebar, mobile bottom navigation, adaptive article cards, and a working search bar for both desktop and mobile — without Bootstrap or Tailwind — pushed me to deeply understand CSS layout systems.
Built With
- api
- atlas
- axios
- bcryptjs
- css3
- dotenv
- express-validator
- express.js
- gnews
- html5
- javascript
- json
- mediastack
- mongodb
- mongoose
- newsapi
- node.js
- nodemon
- rest
- thenewsapi
- tokens
- vanilla
- web
Log in or sign up for Devpost to join the conversation.