// PROJECT_
Savora
01. LIVE_DEMO
// LIVE PREVIEW
Savora →
02. PROJECT_OVERVIEW
A mobile recipe app built around one question: what can I cook with what I have? Type in your ingredients, pick a diet filter (Vegan / Vegetarian / With meat), and get ranked suggestions instantly from a local catalog — enriched with results from Spoonacular's much larger online database when local matches run thin. The centerpiece is Linna, a Claude-powered AI assistant with the app's plush-sheep mascot as her face. She's a real conversational agent, not a canned chatbot: she calls tools to search the local recipe catalog first, and only reaches for the (quota-limited) Spoonacular search when local results are weak or the user explicitly asks for more — the same two-source logic the manual search screen uses, but decided by an LLM in real time instead of a fixed if/else. Every recipe she surfaces — local or external — links straight into the app via deep navigation. The backend was rewritten mid-project from Node/Express to Python/FastAPI — a deliberate choice to work with something new, not a technical necessity — mirroring the original API 1:1 with full test coverage (73 tests) before the assistant was added on top. It runs on Railway with an embedded SQLite catalog that self-creates and self-seeds on every boot, no manual migration step. Every external API key (Anthropic, Spoonacular, NewsData) lives server-side only — the client never touches a paid key directly. The Home screen was redesigned around the mascot and real photography. Still in active development, working through Google Play's publishing process ahead of a public release. A dev+QA practice project, paired daily with Claude (Sonnet 5) as engineering partner — from the backend migration and assistant architecture through the redesign and deployment debugging.
Status
Active Development
Scope
Full-Stack Architecture
Primary Stack
Expo SDK 54
Repo
Public
03. SCREENSHOTS




04. TECH_STACK & ARCHITECTURE
//Linna (AI assistant): Claude tool-calling loop server-side — searches the local catalog first, Spoonacular only when needed — client only sees chat turns + recipe matches
//Expo SDK 54 with expo-router file-based tabs: Home, Cook (ingredient search + diet filter), Shopping, Favorites
//Ingredient matching engine: scored by matched/total ratio, diet hierarchy (vegan ⊂ vegetarian), synonym resolution — shared by both the manual search screen and Linna's local-search tool
//FastAPI backend on Railway, SQLite self-seeds on boot, every external API key stays server-side
//Home screen and recipe detail screens redesigned around the mascot and real photography
//In active development, working through Google Play's publishing process ahead of a public release
Linna — Claude Tool-Calling Assistant
AI — Conversational Recipe Agent
The app's plush-sheep mascot is the face of a real Claude-powered agent, not a canned chatbot. She has two tools — search the local catalog, and search Spoonacular — and decides herself which to call: local first, Spoonacular only when local matches are weak or the user explicitly asks for more. The tool-use loop (detect tool_use → execute locally → feed results back → repeat until a final reply) runs entirely server-side; the client only ever sees plain user/assistant chat turns plus the recipe matches to render as tappable chips.
Expo SDK 54 + expo-router
Frontend — Mobile Framework
File-based routing via expo-router — Home, Cook (ingredient search), Shopping, Favorites tabs plus recipe detail routes. TypeScript throughout from screen components to API call types.
react-native-reanimated ~4.1.1
Frontend — Animations
FadeInDown animations on recipe cards and news items, spring-based bounce on pressable buttons, and the mascot's idle breathing/blink loop.
expo-image
Frontend — Image Handling
Drop-in Image replacement with built-in memory cache and blurhash placeholder support — fast, non-blocking recipe and news thumbnails.
FastAPI + Python
Backend — API Server
Rewritten mid-project from Node/Express to Python/FastAPI — mirrors the original API 1:1 (routes, validation, status codes) with 73 tests covering matching logic, API routes, and the assistant's tool-calling loop before anything shipped.
SQLite (stdlib, self-seeding)
Backend — Database
Embedded SQLite that creates its schema and seeds the recipe catalog on first boot — no manual migration step, survives Railway's ephemeral filesystem fine since the catalog is fixed reference data, not user state. check_same_thread=False handles FastAPI's threadpool-executed sync routes safely.
Spoonacular + NewsData — server-side only
Backend — External APIs
Both Spoonacular recipe search and NewsData food/health news are proxied entirely server-side. Every paid API key (Anthropic, Spoonacular, NewsData) lives in Railway env vars — none of them are ever bundled into the client, so a decompiled APK exposes nothing.
Railway
Deployment
Backend deployed via Railway's Railpack builder with an explicit railway.json start command (Railpack's Python auto-detection couldn't find the FastAPI app behind a factory-function bootstrap). CORS open to the mobile client, redeploys automatically on push to main.
pytest + pytest-asyncio
Testing
httpx.MockTransport mocks Spoonacular/NewsData without real network calls; a fake AsyncAnthropic client tests the tool-calling loop deterministically. 73 tests total across matching logic, API routes, and the assistant.