Building WebScope

:A Customer-Facing IoT Portal in Django, JavaScript, and MySQL

Tom Wade

9/1/20256 min read

When I joined Resensys as a Manufacturing Associate, our infrastructure-monitoring customers got their data through a mix of static reports, custom queries, and direct emails to the team. Three years later, those same customers log into a Django + JavaScript + MySQL web portal I designed and built from scratch — checking real-time sensor data, configuring threshold alerts, browsing historical archives, and integrating with their own systems via REST API. This is the story of how I got there: the architecture decisions I made early, the ones I'd make differently today, and what I learned building software that customers I'd never meet were going to use every day.

The problem we were solving

Resensys deploys wireless sensors for structural health monitoring — strain, tilt, vibration, crack-width, displacement — across bridges, buildings, fulfillment centers, dams, and other infrastructure. Customers care about three things: what's the sensor reading right now, how is that reading trending over time, and when do I need to do something about it. Before WebScope, getting answers to those questions was a manual process. We'd run queries, generate reports, and email back charts. It worked, but it didn't scale, and it put us in the loop on every customer interaction with their own data.

The brief I had was modest: build a self-serve portal that lets customers see their data without us in the middle. The reality of what we needed turned out to be broader. Customers wanted real-time visibility, not just static reports. They wanted threshold-based alerts when readings crossed configurable limits. They wanted to look at historical data and zoom into specific events. They wanted to pull data into their own systems via API. And they wanted documentation good enough that their own internal teams could onboard new users without calling us.

Why Django + JavaScript + MySQL

I made the stack decision early, and I'd make most of it the same way today. A few of the considerations:

  • Django for the backend. Python is what I work in daily, and Django gives you so much for free — ORM, admin interface, authentication, migrations, REST framework integration. For a small-team production application that needs to be maintained by one or two engineers, the productivity gain is enormous. The alternative I considered was Flask, which we use for smaller internal tooling, but Flask's strength (minimal opinions) becomes a liability when you're building a real customer-facing product where you actually want opinionated defaults.

  • MySQL for the data layer. We had existing time-series sensor data already stored in MySQL on the device-side infrastructure. Reusing the same database engine simplified everything: integration patterns, backup procedures, ops familiarity. A purpose-built time-series database (InfluxDB, TimescaleDB) might have been faster for analytics queries, but the operational cost of running two database systems was higher than the performance gain we'd see at our data volumes.

  • JavaScript front-end frameworks. For interactive charts and dashboards, server-rendered HTML alone wasn't going to cut it. The choice of which JS framework was less important than committing to one. The portal needs real-time updates, customer-side state management, and rich visualization — all of which is easier in a modern JS framework than rolling it yourself.

  • REST API as a first-class feature, not an afterthought. From day one, anything the web UI could do, the API could do. Customers integrating WebScope into their own dashboards or alerting systems shouldn't have to scrape HTML. Django REST Framework made this nearly free, and I treat the API as a published contract.

Architecture, simplified

At a high level, the system has three layers. Sensors and gateways send data through cellular backhaul into our cloud infrastructure, where it lands in MySQL alongside metadata about the device, customer, and deployment. Django reads that data, applies access control, and serves it to the customer through both a web UI (rendered with templates plus a JavaScript front-end on top) and a REST API. Customers configure their own alert thresholds in the UI; an alerting service polls for new data, evaluates against configured thresholds, and dispatches notifications.

The thing that took me longest to get right was the access control model. Sensor data is tied to specific deployments and specific customers; some customers have multiple deployments; some deployments are shared between customers; some users at customer organizations should see everything, while others should only see specific sites. Getting this right meant being careful about what queries the ORM was allowed to run on behalf of a logged-in user. I did not get this right on the first try, and the refactor that fixed it was one of the more educational moments of the project.

The discipline of being the only developer

Being the sole developer of a customer-facing application changes how you must work. There's no senior engineer to catch your bad decisions. There's no on-call rotation to spread the support load. There's no design review where someone tells you the schema you want will hurt you in 18 months. So you have to be your own check on those things, which means slowing down.

The two practices that made the biggest difference for me were writing tests for anything customers actually depend on, and keeping the public API stable even when refactoring internals. Customers don't care about my internal class names or how I'm storing things in the database. They care that the endpoint they're hitting today returns the same shape tomorrow. That commitment forces you to think about your interfaces deliberately, which is good discipline whether you're a solo developer or one of fifty.

Customer feedback as the architecture's compass

WebScope iterates on direct customer feedback. Most of the features that exist today — threshold-based alerting, historical zoom views, customer-configurable dashboards, the API — came from real customer requests. Some came from support cases (“why can't we see X?” turning into a feature). Some came from sales conversations. Some came from me sitting with customers on-site during deployments and watching what they actually did with the data.

The thing I'd tell anyone starting a customer-facing application: build the feedback loop first, then build the product. If you don't have a structured way to hear from real users, you'll build a product that satisfies your imagined user, who is almost certainly different from the actual one. WebScope's first six months were as much about hearing from customers as they were about writing code.

The documentation as a feature

I authored the public-facing user documentation alongside the application. It's hosted at a public URL [resensys.net:8443/docs/] and customers self-serve from it daily. The documentation is, in a real sense, part of the product. When a customer hits a question they can't answer themselves, two things happen: they read the docs, and (if the docs don't answer it) they file a ticket. Good docs reduce ticket volume, which means I have more time to actually build the product instead of explaining it. Good docs also reduce onboarding friction for new customer staff, which means deployments stick better.

Documentation is also a place where the discipline of writing exposes the places where the product itself is confusing. I have refactored more than one piece of WebScope because writing the documentation for it forced me to admit it was harder to use than it needed to be.

What I'd do differently

If I were starting WebScope over today, knowing what I know now, a few things would change. I'd commit to infrastructure-as-code earlier; managing the AWS resources through the Console worked but doesn't scale to a larger team. I'd use TypeScript on the front-end from the start; I added it midway and the migration cost outweighed the early-stage friction would have. I'd invest in observability earlier; the first version of WebScope had basic logging and not much else, and I learned the hard way that good observability is what lets you trust your own product in production.

I'd also start with a more deliberate access-control schema. The model evolved organically as customer requirements emerged, and the eventual refactor cost more than building it right would have. There's a general lesson in there: identity, authentication, and authorization are the parts of an application that get expensive to change late, and they're worth the time up front.

What WebScope taught me

Looking back, the most valuable lesson from building WebScope wasn't a technical one. It was about ownership. When you're the only person who can fix something, you fix it differently than when you can ask someone else. You learn to read your own logs more carefully, to write code you can debug six months from now, and to take customer feedback seriously because you're the one who'll have to live with the consequences. Those instincts have carried into everything I've worked on since.

WebScope is still running, still iterating, still being used by customers worldwide. The codebase has aged better than I expected, the customers seem genuinely happy with it, and the documentation I wrote three years ago still answers most of the questions that come in. That's about the best outcome I could ask for from a project that started as one engineer trying to give customers what they actually needed.