## 3 System Architecture
### Backend API Layer  
The ExpressJS backend serves as the intermediary between the data and the frontend client. It exposes a set of RESTful API endpoints over HTTP(S). Key API endpoints include the following:  
* /search (or/listings), which accepts filters (location radius, price range, keywords) as query parameters and returns a JSON of matching listings (potentially already sorted by some criteria).
* /recommendations, which triggers the recommendation engine for a given user context, returning a curated list of listings.
* /stats, which provides aggregated data (for charts), such as counts per neighborhood or price distribution.  
Our recommendation endpoint extends a basic listing lookup with a transparent \"explainability\" layer that surfaces why each rental is a good fit. When a GET request hits \"/api/rentals/recommendations\", we first retrieve raw recommendations via a getRecommendations function, passing along any filters or preferences from the query string (for example, the target university or a user's budget). We then iterate over each recommendation and compute four distinct explainability scores: a proximity score based on the distance from the property to the specified university, a price-value score that evaluates how the listing's rent compares to the user's budget, a freshness score that rewards recently scraped listings (e.g., those posted within the last week), and a text relevance score that measures how closely the listing description matches a set of university-related keywords. In parallel, we assemble human-readable \"explanation tags\" (like \"Near Campus\" for high proximity scores or \"Below Budget\" when the rent is under 90% of the budget), using a tagging function informed by DQNViz principles [1]. DQNViz, originally designed to visualize Q-values and policy decisions in reinforcement learning, is here repurposed to map hidden decision weights into these intuitive labels so that, for instance, any recommendation with a proximity score above 0.8 gets tagged \"Near Campus\", and any listing less than seven days old is flagged \"Recently Listed\". Finally, the enriched recommendations, including both their original data and our new explanation factors and explanation tags, are returned as a JSON array, giving users not just a ranked list but a clear breakdown of why each option was selected.  
The backend implements the logic described in the Methodology. When a recommendation request comes in, the backend queries MongoDB for candidates and then invokes the RL model to rerank the results, as shown in Figure 3.  
The RL models logic in Python for ease of use with existing ML libraries (like PyTorch 2.7.1 or TensorFlow 2.19.0) and exposes it via a microservice or a Python script triggered server-side [10]. The Express server and the Python RL module communicate either through a direct binding, using a Node.js Python bridge library, or via an intermediate step consisting of precomputing recommendation scores for listings that the Node server is then using to sort results. The Express server also orchestrates calls to the data layer for creating the GeoJSON outputs used by the frontend map. For example, an endpoint/GeoSON/neighborhoods might deliver colored polygons with computed properties(like average rent) for each area. Because Node.js is proficient at I/O and MongoDB queries are asynchronous, the backend can handle multiple requests in parallel, which is necessary for real-time interactive use [11].