// PROJECT_

Savora

Expo SDK 54

01. LIVE_DEMO

▶Tap to play
// DEMO
PAUSED

// LIVE PREVIEW

Savora →

VISIT_SITE

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

Home — Today's picks
Home — Today's picks
What to cook? — local + Spoonacular results
What to cook? — local + Spoonacular results
Recipe detail — Spoonacular
Recipe detail — Spoonacular
Shopping list
Shopping list

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.