NutriKor
A nutrition diary that replaced a 189-node automation workflow with a real service — and fixed the bug where confirming an old message saved the wrong meal.

The impact:
What this product is.
NutriKor is a nutrition diary that lives inside Telegram: describe a meal in chat, the agent resolves it against real nutrition databases, and a confirmed entry rolls up into daily totals, goal tracking and period reports. A Mini App gives the same data a proper interface.
What needed solving.
The original bot was a 189-node n8n workflow, and it worked until it didn't. A meal awaiting confirmation existed only in the agent's in-process conversation buffer — eight turns deep — so a draft silently vanished after a few messages. Worse, the inline confirm button carried the bare payload “confirm”, with no draft identity, so tapping an older message could confirm whichever draft happened to still be in the window and save the wrong meal entirely.
How we approached it.
We rebuilt it as a NestJS service where a draft is a Firestore document with a stable id, and that id travels inside the callback payload itself. A button pressed three days later resolves to exactly the draft it was rendered for and lands on its own date. Because the saved record id is the draft id, a repeated confirm returns the existing row instead of creating a duplicate. Nutrition lookups fall back deterministically from USDA to Open Food Facts to an explicit not-found, so the agent never invents numbers.
What's inside?
Each pending meal is a Firestore document with a stable id, so it survives restarts and is still confirmable days later.
The draft id travels in the callback payload, so a button always resolves to the meal it was rendered for — on its own date.
The saved record id is the draft id, so pressing confirm twice returns the existing entry rather than duplicating the meal.
USDA FoodData Central first, Open Food Facts second, explicit not-found third — the agent never fabricates a calorie count.
A Vite and TypeScript Mini App authenticates from real Telegram initData, exchanged for a JWT before any REST call.
Lint, unit tests, build, end-to-end, App Engine deploy with no traffic, health check, then a traffic shift that rolls itself back on failure.
Tools change. Standards don’t.
We choose the stack around the product — not the other way around.
- NestJS
- TypeScript
- Telegram Bot API
- Telegram Mini App
- Vite
- Firestore
- Google App Engine
- Firebase Hosting
- zod
- USDA FoodData Central
- Open Food Facts
- GitHub Actions
NestJS, TypeScript, Telegram Bot API, Telegram Mini App, Vite, Firestore, Google App Engine, Firebase Hosting, zod, USDA FoodData Central, Open Food Facts, GitHub Actions
What we handled.
We worked on the product development and implementation, focusing on building a polished, functional and user-friendly experience aligned with the goals of the product.



