← All Projects

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.

IndustryHealth & Nutrition
Product TypeTelegram Mini App
PlatformTelegram
BackendNestJS on App Engine
ResultA no-code prototype turned into a durable product
NutriKor project hero image

The impact:

189
n8n nodes retired
2
Nutrition data sources
Draft lifetime
0
Duplicate confirmations possible

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.

PROJECT FACTS
IndustryHealth & Nutrition
Product TypeTelegram Mini App
PlatformTelegram
BackendNestJS on App Engine
ResultA no-code prototype turned into a durable product

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?

Drafts that outlive the conversation

Each pending meal is a Firestore document with a stable id, so it survives restarts and is still confirmable days later.

Confirmation that cannot misfire

The draft id travels in the callback payload, so a button always resolves to the meal it was rendered for — on its own date.

Idempotent by construction

The saved record id is the draft id, so pressing confirm twice returns the existing entry rather than duplicating the meal.

Deterministic nutrition resolution

USDA FoodData Central first, Open Food Facts second, explicit not-found third — the agent never fabricates a calorie count.

Mini App with verified sessions

A Vite and TypeScript Mini App authenticates from real Telegram initData, exchanged for a JWT before any REST call.

Canary deploys with automatic rollback

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.

Replacing a no-code workflow with a service
Agent and tool-calling design
Draft state machine and data modelling
Mini App frontend
Deployment pipeline with canary rollout