Back to projects
Full-stack Product Archived 2022

TowerOpedia

MERN app where each building owner holds a collection of nested property records. Mongoose nested schemas, Leaflet map views, custom dark theme over MUI.

Nested schema design, end-to-end

  • TypeScript
  • React
  • Node.js
  • Express
  • MongoDB
  • Leaflet

Why I built this

This was the first project I built end-to-end where the data model had real shape. Clients (building owners) own collections of buildings, each building has its own metadata and a location. CRUD on a flat list isn’t very interesting. CRUD on a parent-child schema with maps and a dark mode is more fun.

The brief said the UI shouldn’t include create or edit for clients. That ended up shaping a lot of the design. Client management lives on the server (you can hit it via Postman) and the front-end focuses on the building-level CRUD that owners actually need.

What it does

  • Pick a client at login to load their building portfolio.
  • Add, edit, delete, view buildings within the active client’s scope.
  • A map view rendered with react-leaflet, showing every building’s location next to the list.
  • Dark mode through a custom MaterialUI theme. Toggleable, and the rest of the chrome adapts cleanly.
  • Live: Netlify deploy preview.

Architecture

The data model is the thing worth looking at. Each Client document embeds a list of Building documents. Mongoose’s nested schemas let the parent-child relationship live on the parent instead of being a separate join collection.

const BuildingSchema = new Schema({
  name: { type: String, required: true },
  address: String,
  coordinates: {
    lat: Number,
    lng: Number,
  },
  notes: String,
}, { timestamps: true });

const ClientSchema = new Schema({
  username: { type: String, required: true, unique: true },
  buildings: [BuildingSchema],
}, { timestamps: true });
Edit flow: the building form writes back through the same Mongoose schema that powers the list and map views.

Technical decisions

Embed buildings inside clients. A separate buildings collection would have meant an explicit join on every read. Reads are mostly parent → children, so embedding kept queries to one round trip.

Leaflet over Google Maps. No API key, no quota, OpenStreetMap tiles for free. react-leaflet is a thin wrapper that doesn’t fight React’s lifecycle.

Custom MaterialUI theme. The default MUI palette is loud for a CRUD UI. The custom theme dials accents back and gets dark mode for the cost of one extra option object.

Postman for client management. Building a UI for the out-of-scope client flow would have been busywork. A REST endpoint and a Postman collection cover it.

What I learned

Embedded subdocuments are great until you need to query across parents. Searching for a building across all clients, for example. Fine for the brief but the first thing I’d revisit.

MaterialUI’s theme system rewards investment. Once the palette is set, every component picks it up. Trying to override per-component is the slow path.

Leaflet’s clustering plugins are the difference between “looks fine with 5 buildings” and “looks fine with 500”. Worth knowing about before you need them.

Next steps

  • Reshape the data model into a normalised clientsbuildings pair. Embedded subdocuments hit their ceiling fast once cross-parent search shows up.
  • Replace the Netlify preview with a stable host (Cloudflare Pages plus a small worker for the API).
  • Swap react-leaflet for MapLibre with vector tiles. Better performance at scale, no quota concerns.
  • Pull the building card / list / map composition into a small reusable kit for similar projects.

Source: github.com/uzairali19/toweropedia.