NewsLens
A full-stack news aggregation and analysis application built to transform external news data into an application-owned system for discovery, persistence, saved stories, internal article experiences, and future AI-assisted context.
More than a feed of external links.
NewsLens began with a simple idea: make current news easier to explore. The project has since developed into a larger software-engineering exercise focused on building clear boundaries between external services, application logic, persistence, API design, frontend data management, and presentation.
Instead of allowing the frontend to call a third-party news API directly, NewsLens owns the flow of its data. Articles move through a backend provider adapter, are converted into a consistent internal format, persisted in MongoDB, exposed through the application's own REST API, and consumed by the React frontend.
That architectural decision allows NewsLens to support features that would be difficult to build reliably on top of a frontend-only API integration, including internal Story pages, persistent saved stories, AI summary caching, historical retrieval, and eventually user-specific libraries and event-based news analysis.
A layered pipeline from external provider to interface.
NewsLens is organized as an npm workspace monorepo containing a React client and a Node/Express server. The server owns provider communication, business logic, validation, persistence, and API responses while the frontend is responsible for the user experience.
The result is a system in which each layer has a defined responsibility rather than allowing networking, database access, provider behavior, and presentation to accumulate inside the same components or route handlers.
Treating external vendors as replaceable implementations.
NewsLens does not make the rest of the application depend directly on NewsAPI.ai. Instead, the backend defines a news-provider abstraction and places provider-specific behavior behind that boundary.
This became especially important when the deployment requirement changed to a strict zero-cost demonstration target. A provider can work well technically during development while still becoming a poor long-term fit because of pricing, quota limits, or service changes.
By depending on a provider interface rather than one vendor's API, NewsLens can replace or supplement its ingestion strategy without rewriting its persistence layer, API routes, Story pages, saved system, or frontend architecture.
External data becomes NewsLens data.
One of the central architectural boundaries in NewsLens is article normalization. External APIs have their own response structures, naming conventions, optional fields, and source metadata. Those implementation details should not dictate the rest of the application.
The provider therefore maps external responses into an internal article representation before that data travels deeper into the system.
Article
├── id
├── title
├── description
├── body
├── imageUrl
├── publishedAt
├── url
└── source
├── id
├── name
└── country
Architectural representation based on the NewsLens article contract.
Once normalization is complete, the frontend and internal services can work against the NewsLens representation rather than knowing how NewsAPI.ai structures its payload.
WITHOUT NORMALIZATION
Provider response structure leaks across the application.
UI logic becomes dependent on a third-party API.
Changing providers creates widespread refactoring.
WITH NORMALIZATION
Provider-specific translation happens at one boundary.
Application code works with predictable article data.
External implementations remain replaceable.
Persisting articles instead of treating them as disposable API responses.
Retrieved articles are stored in MongoDB Atlas. Persistence changes NewsLens from a temporary API viewer into an application that can build additional functionality around the same stories.
INTERNAL STORY PAGES
Persisted articles can be retrieved later through NewsLens' internal article routes instead of depending on the original provider response remaining available.
SAVED STORIES
A saved library can reference persistent article records and survive browser refreshes.
AI SUMMARY CACHE
Future generated context can be stored alongside an article rather than regenerated every time a story is opened.
FUTURE ANALYSIS
Persistence creates a foundation for related-story grouping, event analysis, historical retrieval, and personalization.
BULK UPSERTS
Articles are identified through a provider article ID. Instead of blindly inserting every provider result each time the feed refreshes, NewsLens uses upsert-style persistence so existing records can be updated while new records are inserted.
Keeping HTTP, business logic, and database access separate.
The backend is built with Node.js, Express, TypeScript, MongoDB, Mongoose, and Zod. It is intentionally divided into layers so that route handlers do not become responsible for every part of a request.
REPOSITORY LAYER
Database implementation details are kept out of Express route handlers. Article lookup and persistence are handled through the article repository while saved-story persistence is handled through its own repository.
This means an HTTP route can express an application intention such as "retrieve this article" without also containing the exact Mongoose query required to retrieve it.
SERVICE LAYER
The service layer coordinates behavior that spans multiple dependencies. Retrieving top news, for example, involves requesting data through the provider, normalizing it, persisting it, and returning an application-owned response.
Designing an API around application resources.
NewsLens exposes its own REST endpoints rather than requiring the React frontend to communicate with external news services directly. The API supports top-news retrieval, individual persisted articles, saved stories, saved status, and save/remove operations.
STRUCTURED ERRORS
API failures use structured responses instead of arbitrary error strings. This creates a more predictable contract for the frontend and allows different failure conditions to be represented explicitly.
{
"error": {
"code": "ARTICLE_NOT_FOUND",
"message": "The requested article was not found."
}
}
Invalid article IDs are distinguished from valid IDs that simply do not correspond to an existing article. Provider failures are also handled separately from unexpected application failures.
Catching invalid state at system boundaries.
NewsLens uses Zod for runtime validation while TypeScript provides compile-time contracts during development. The project treats those responsibilities as complementary rather than interchangeable.
ZOD
Validates data while the application is running.
Checks incoming query and route parameters.
Rejects malformed requests before deeper application layers.
TYPESCRIPT
Checks assumptions during development.
Provides explicit application contracts.
Improves editor feedback and refactoring safety.
Reusable Express middleware validates article IDs for multiple endpoints instead of duplicating identical logic across route handlers.
FAIL-FAST ENVIRONMENT CONFIGURATION
Server configuration is loaded from environment variables and validated during startup. Required values include the runtime environment, port, client origin, news-provider key, and MongoDB connection string.
If required configuration is missing or malformed, the backend fails during startup instead of running in a partially configured state. Real credentials stay in the local server environment and are not intended for source control.
Separating data access from presentation.
The frontend is built with React, TypeScript, Vite, Tailwind CSS, TanStack Query, React Router, motion/react, and Axios, with some backend communication also using the native Fetch API.
API requests are being moved away from page components and into dedicated API functions and query hooks. For example, Story retrieval follows a responsibility chain rather than placing the entire asynchronous request directly inside the page.
TANSTACK QUERY
TanStack Query manages asynchronous server state including loading, errors, caching, query invalidation, mutation state, and synchronization between the server and the interface.
This keeps server-owned data from being treated as ordinary local component state and gives features such as saved stories a consistent way to update the UI after mutations.
Moving from outbound links to an internal story experience.
Early versions of NewsLens linked article cards directly to their original publishers. The application later evolved to include internal Story pages.
Story pages can present source information, publication date, image, description, article content, saved status, and a link back to the original publisher.
Keeping the original source available preserves attribution while creating space for NewsLens to eventually add its own structured context around a story.
Persistent interaction instead of temporary UI state.
NewsLens includes persistent saved stories. Saving an article does not merely toggle a React boolean or write an ID to browser storage. The action travels through the application stack and is persisted in MongoDB.
The dedicated Saved page supports loading, error, empty, and populated states. Saved cards route back into their corresponding internal Story pages.
Preserving saved-story order across database queries.
One subtle issue appeared when loading the saved library. Saved records are retrieved newest-first, but MongoDB's $in query does not guarantee that matching article documents will be returned in the same order as the supplied IDs.
Simply requesting all matching articles could therefore produce a library whose order did not match the user's saved-record order.
This produces deterministic ordering without relying on behavior the database does not promise.
Designing around a real deployment constraint: $0.
One of NewsLens' most important evolving requirements is that the final portfolio demonstration should be capable of operating within free service tiers.
The objective is not to pretend a high-traffic production news platform could operate indefinitely at no cost. The goal is to demonstrate a complete deployed architecture at portfolio scale without requiring ongoing paid infrastructure.
REPLACEABLE PROVIDERS
News ingestion and AI generation sit behind interfaces so pricing changes do not force an application rewrite.
CACHE EXPENSIVE WORK
Generated AI context is intended to be persisted instead of regenerated for every request.
MINIMIZE API CALLS
Persisted articles and cached output reduce unnecessary requests to external services.
GRACEFUL FAILURE
Exhausting a free-tier quota should disable only the affected capability rather than taking the entire application offline.
NewsAPI.ai was useful for developing the current ingestion pipeline, but the final free demonstration may supplement or replace it with a free ingestion strategy such as RSS. Because provider-specific behavior is already isolated, the rest of the NewsLens architecture can remain largely unchanged.
Preparing AI context without coupling the application to one model.
AI summarization infrastructure is currently in progress. NewsLens is being designed to provide structured article context rather than treating AI output as an unrestricted block of generated text.
AI Summary
├── Summary
├── Why It Matters
├── Key Points
├── Unanswered Questions
├── Generated At
└── Model
This represents the planned structured summary contract; generation is still under active development.
The AI system follows the same architectural principle as news ingestion. A SummaryProvider interface separates application behavior from the underlying AI vendor.
AI SUMMARY CACHING
Generated summaries are intended to be stored with their associated article. If another request needs context for the same story, NewsLens can return the cached result instead of paying the latency and quota cost of another model invocation.
This becomes particularly important in a free-tier architecture because one successful generation can serve multiple future requests.
Refactoring toward stronger boundaries without stopping development.
NewsLens has been developed incrementally rather than attempting to predict the final architecture before the product existed.
Story retrieval is one example. The feature originally lived more directly inside the Story page and was later separated into the page, a dedicated query hook, and the frontend API layer while preserving the working behavior.
This approach avoids architecture for architecture's sake while still moving the codebase toward more maintainable production-style patterns.
A functioning application foundation with room to evolve.
NewsLens has progressed beyond a frontend news prototype. The current application can retrieve provider articles, normalize them, persist them in MongoDB, expose them through its own API, display them through React, open persistent internal Story pages, and save stories to a persistent library.
IMPLEMENTED
IN PROGRESS / PLANNED
From individual articles to understanding developing events.
The long-term goal for NewsLens is not simply to show a larger list of articles. The larger idea is to help users understand what is happening, how multiple sources are covering an event, why the story matters, what remains unanswered, and how the event changes over time.
That direction builds naturally on the architecture already in place: normalized articles, persistence, provider abstraction, internal Story pages, shared application data, and structured AI output.
The project therefore serves two purposes at once: building a useful news product and demonstrating engineering decisions across frontend development, backend architecture, databases, external API integration, validation, persistence, cost constraints, and AI-assisted software design.