Easy Breathe
Serbia’s public pollen readings, narrowed to the ones a single person needs to know about.
- Year
- Context
- Domain
How one request runs
- Entry: Five open-data endpoints
- Orchestration: Scheduled seeding - NestJS
- In parallel
- Monthly - Allergens, types, locations
- Hourly, 9 to 12 - Pollens, concentrations
- Output: One deduplicated database - Narrowed to a radius, a day and your allergens - MongoDB
Overview
The public data is there and it is good: Serbia’s environmental agency publishes daily pollen readings from measuring stations across the country, as an open API with no key and no limit. What it does not do is tell one person with a ragweed allergy whether today is a bad day where they happen to be standing. That gap is the whole app.
So the work splits in two. The API mirrors the open data into its own database on a schedule, because a phone should not page through hundreds of thousands of national measurements to answer a local question. The app then asks one question against that mirror - what is in the air near me, of the things I react to - and answers it on a map, in four levels, and in a notification.
Architecture
The open data is mirrored on two clocks
The five endpoints do not change at the same rate, so they are not fetched at the same rate. Allergens, allergen types and locations are seeded at nine on the first of the month. Pollens and concentrations, the ones that move, are fetched every hour between nine and noon, when the day’s measurements appear.
Seeding is safe to repeat
Every seeder looks up which ids it already holds, keeps only the ones it does not, and inserts those. So an hourly job that finds nothing new writes nothing, the same run can be repeated without duplicating a measurement, and the window it asks for reaches a week back, far enough to pick up a reading published a few days after it was taken.
A location and a radius become a set of stations
The user’s coordinates and their chosen radius in kilometres go into a MongoDB geospatial query. The radius is first divided by the earth’s own, because the query takes it in radians. What comes back is every measuring station close enough to matter to that person.
Stations and a date become the readings that count
Those station ids and today’s date select the day’s pollen records, each of which carries the ids of the concentrations measured with it. Those concentrations are then fetched and cut down to the allergens the user actually selected, so the response holds only readings that are both nearby and relevant.
Each reading gets a level and a place
A number in the air means nothing on its own, so each concentration is compared against its own allergen’s published thresholds and comes out as Low, Normal, High or Very high. The reading is then formatted with the allergen and the station it came from, which is what the map marker and the detail view show.
What it does
Pick your own allergens
Around thirty are published, and the profile lets you select as many of them as you like. Everything downstream - the map, the levels, the notifications - follows that list.
Your radius, your interval
The search radius in kilometres and the check interval in hours, both set on the profile. A shorter interval means fresher alerts, a longer one saves battery.
A map you can read at a glance
Markers on the measuring stations near you, coloured by level. Tap one and it lists which of your allergens were measured there and how high each was.
Four levels, with counts
Low, Normal, High and Very high, each with the number of nearby allergens at that level. Tap a level for the list, tap an entry for the station, the description and the reading.
A notification when it climbs
A push notification and an in-app alert when something in the selected list reaches a high concentration nearby, so the app is useful without being opened.
Accounts, kept simple
Email sign-up and sign-in, with the profile that holds the allergens, the radius and the interval, so the same selection follows the account rather than the phone.
What I took from it
The lesson that stuck was that open data is not the same as usable data. With five endpoints that reference each other by id, a national scale, and no way to ask a geographic question, the value is entirely in the mirroring and the joining. Deciding what to copy, how often, and how to make copying it twice harmless is where the engineering actually was.
The other half is that the answer has to arrive without being asked for. Someone with an allergy does not open an app to check - they want to be told, at a radius and an interval they set once. Push notifications on top of a scheduled backend are what turn a public dataset into something that reaches a person on the day it matters.