Skip to content

Client work · Fibre rollout · Web · API

Dynet

A planning and tracking system for fibre-to-the-home installations in Dutch housing districts

Districts arrive as the network operator's Excel sheet; Dynet turns them into buildings, flats and a work queue for six specialist roles — surveyors, planners, installers — with calendars, floor plans down to the cable, and live progress per district. API, web app and import pipeline, built by me.

Engineering write-up: Design notes from Dynet: Excel as the integration contract, transactional imports, and six roles on one API
Dynet dashboard: the City → Area → District → Building → Apartment hierarchy with counts, the five-step installation process flow, and completed, pending and scheduled installation totals

Connecting a housing district to fibre is a relay. The network operator hands a contractor a list of addresses. Someone has to plan a technical survey of every building, someone has to walk the building and decide where the cable goes, someone has to prepare the materials, someone has to book an appointment with each resident, and someone has to show up with a drill and finish the job. Every hand-off is a place where a status goes missing when the whole thing lives in spreadsheets, group chats and phone calls.

Dynet puts the relay in one system. I’ve been building it for a Dutch fibre installation contractor since October 2023 — the API, the web app and everything in between — and it’s the project I point to when someone asks what “backend work” means in practice.

What it does

The data model is the country: City → Area → District → Building → Flat. A district comes in as the operator’s Excel sheet and becomes a set of buildings with their flats. Each flat then moves through five stages, each owned by a different role:

Role (Dutch)Who they areWhat they do in Dynet
Technische PlanningTechnical plannerBooks the technical survey; owner and key-holder contacts; notes
Technische SchouwerTechnical surveyorRecords cable direction and riser point, uploads the report, signs off
WerkvoorbereiderWork preparationMaterials, cable type, lift needed, monument status, feed-through dependency
HAS PlanningInstallation plannerBooks the installation appointment with the resident
HAS MonteurInstallerSees today’s jobs, completes the install with photos
AdminOperationsUsers, cities and areas, imports, priorities, everything above

Everyone logs into the same app and sees a different one: their own navigation, their own apartment form, their own calendar. An installer never sees the survey form; a planner never sees the installer’s photo upload.

Dynet operations overview dashboard

Districts and buildings

The district page is the daily view. Filter chips split buildings by type (high-rise, duplex, low-rise), by state (with appointment, completed, pending, blocked), and a completion bar tracks the district as a whole. Each building card draws itself floor by floor from its flats — finished flats green, pending grey — so a glance across a row of cards tells you which building the team should go to next. Districts can be reordered by drag and drop to set the priority the operator has agreed.

District view with building cards

Building layouts, down to the cable

Fibre isn’t installed per flat; it’s installed per riser. So a building has a layout: one or more blocks, each with a wing type (stairs left, stairs right, no stairs, apartment blocks, wings without a ground floor — twelve schemas in total) and a top floor. Step one draws the cross-section. Step two maps every floor of that drawing to a flat, a cable number and a cable length. Step three lets a planner pick a cable and schedule the installation for exactly the flats hanging off it.

Building layout editor

Mapping floors to flats and cables

Scheduling

Two calendars — technical surveys and installations — with one colour per worker and a filter to show a single person’s week. Appointments are booked per building: tick the flats, choose date, time window and person, save. The apartment page then shows every stage in one place: who surveyed it, when the installation is, what the resident was told.

Technical planning calendar

Apartment detail page

Under the hood

API. Express on Node.js, MongoDB through Mongoose, thirteen collections. Every /api/* route sits behind a JWT check; the access token travels in an httpOnly cookie (not localStorage), a refresh cookie rotates it, and a role middleware compares the token’s role codes against what each route allows. Separate rate limits for login, general API calls and file uploads; Helmet, a CORS allow-list from the environment, request sanitisation and express-validator on inputs; one error handler so controllers stay thin. Winston writes structured logs with daily rotation. /health, /health/live and /health/ready exist for the platform to poll, and the whole surface is documented as OpenAPI 3 in Swagger UI, generated from annotations on the routes.

Import pipeline. The operator’s Excel is the integration contract, and it isn’t a stable one — headers change between sheets and between people. The importer matches each required field against a list of Dutch and English synonyms, falls back to fuzzy matching (substring, then Levenshtein within 30%), reports exactly which column it could not find, and previews the parsed rows before anything is written. Rows are grouped into buildings by the postcode_housenumber key inside the order number. The write itself runs inside a MongoDB transaction — district, buildings and flats either all land or none do — while a progress tracker streams steps to the browser over Server-Sent Events so a 2,000-row sheet doesn’t look frozen. Weekly refreshes from the operator go through the same validation in update mode, with a conflict check and an import history per area.

District import form

Web app. React 18 with TypeScript, Material UI, React Router 6 and React Query. Every page is lazy-loaded and wrapped in its own error boundary, so a crash in the calendar doesn’t take the district page down with it. Route guards take an allowedRoles array; the navigation is derived from the same roles. Long building and apartment lists are virtualised; district aggregations are cached for five minutes on the server and invalidated on import. There’s a dark mode because installers use this in vans.

User management with roles

Operations. The API and the web app run on Render against MongoDB Atlas. Seeders can build a complete demo world — users with role colours, apartments, a month of appointments — in one command, which is how new roles get demoed to the client and how the screenshots on this page were made (no customer data appears in them). Jest and Supertest cover authentication, role checks, validation, the security middleware, the error handler and the health probes; React Testing Library covers the front-end hooks and pages.

Swagger API documentation

What I’d do differently

  • TypeScript end to end. The API is plain JavaScript because that’s how the project started. Shared types between the Mongoose models and the React types/ folder would have caught a class of mismatches that tests currently catch instead.
  • Permissions, not roles. Six numeric role codes were the right call for a six-person operation. The moment a client asks for “a planner who can also see installer photos”, codes stop scaling and a permission table starts.
  • A column-mapping step in the UI. Fuzzy header matching removed most import failures; a screen where the admin confirms this column is the house number would remove the rest.
  • A real cache. The in-process five-minute cache is fine for one instance. Two instances means Redis.
  • Replica set from day one, even locally. MongoDB transactions need one. Every new developer machine hits that once.

Working with me on something like this

Dynet is the shape of work I like: an operation that already runs on spreadsheets and phone calls, a handful of specialist roles, and a system that has to earn its place by being faster than the spreadsheet on the first day. If you have one of those, here’s how I work and what I take on, or just email oday@saltserv.com with what the operation looks like.

Questions people ask

What is Dynet?

Dynet is a web-based planning and tracking system for fibre-to-the-home installation work in Dutch housing districts. It imports the addresses a contractor has to connect, models them as buildings and flats, and moves each flat through survey, work preparation, planning and installation with a screen and a calendar for every role involved.

Who built Dynet?

Oday Maleh (Saltserv, Amsterdam) designed and built the whole system as the sole developer: the Node.js and MongoDB API, the React web application, the Excel import pipeline and the deployment on Render and MongoDB Atlas. Development started in October 2023 and continues.

What is the technology stack?

Express on Node.js with Mongoose and MongoDB for the API; React 18 with TypeScript, Material UI and React Query for the web app; JWT authentication in httpOnly cookies with a refresh flow; Swagger for API documentation; Jest and Supertest for tests; Winston for logging; Render and MongoDB Atlas for hosting.

How does the Excel import work?

The network operator delivers each district as a spreadsheet. Dynet matches the column headers against Dutch and English synonyms (with fuzzy matching for typos), validates the rows, groups them into buildings by postcode and house number, previews the result, checks for conflicts with existing data, and then writes everything inside one MongoDB transaction while streaming progress to the browser over Server-Sent Events. Weekly updates from the operator run through the same path in update mode.

Can you build a similar system for my company?

Yes. Internal tools that replace spreadsheets — imports, role-based workflows, scheduling, progress reporting — are exactly the kind of backend and web work I take on alongside my own apps. Email oday@saltserv.com with two or three sentences about the operation you want to model and I'll reply with questions.

Writing about Dynet

Roadie Tuner home screen with the user's instrument profiles and tutorial cards

Roadie Tuner

Music hardware · Android

A free chromatic tuner for twelve instruments and the control centre for Band Industries' Roadie devices: connect over Bluetooth, push firmware, manage instrument profiles and 150+ alternate tunings. I built the Android app; 100K+ installs, rated 4.6 on Google Play.

Case study

Seez store screenshot: Find your next car — buy, lease, new, used — with search results for Toyota models in Dubai

Seez

Automotive marketplace · Android

Seez aggregated used and new car listings across the UAE, Saudi Arabia and Kuwait into one search, priced them with market data, and could identify a car from a photo. I built the Android app — 500K+ downloads — for a company that grew to a million users and was acquired by Pinewood Technologies in 2025.

Case study

PowerGoodies home screen: points balance, a Ready to charge card and an Add new card bonus

PowerGoodies EV Rewards

EV charging · Android

Public chargers are blocked by cars that finished hours ago. PowerGoodies rewards EV drivers with points for charging at partner operators in the Netherlands, Belgium and the UK and for unplugging right after the car is full — points they spend on gift vouchers. I built the Android app end to end: charge map, session tracking, timers, notifications and the rewards shop.

Case study

Bubu AI store screenshot: Meet Bubu, your smart AI companion, with a chat about a daughter waking every hour

Bubu AI

Parenting · early childhood · Android

Bubu is an AI parenting companion: an assistant that answers from your child's own profile, sleep predictions, personalised stories and lullabies, and a nursery hub that connects parents with their daycare. I built the Android app — Kotlin, Jetpack Compose, 20+ Gradle modules — to parity with the iOS app, plus the Android teacher app for nurseries.

Case study