All projects

Case study · 4 min read

Recipia

A cooking assistant you can talk to, show a photo, and plan a week with.

Recipia landing page with the headline "AI-Powered Recipe Discovery and Creation" and a photo of a loaded plate
My role
Built end to end: Next.js frontend and Express/MongoDB backend.
Timeline
Mar 2025 - Feb 2026
Stack
Next.js, React, TypeScript, Tailwind CSS, Zustand, TanStack Query, Express, MongoDB, LangChain, Groq, Google Calendar API, Web Speech API

Recipia is a recipe platform with an AI assistant called ARIA. You can ask it in text, by voice or with a photo, then plan meals for the week, build a shopping list and push the plan to Google Calendar. It also has reviews, favorites, a personal dashboard and an admin side.

The problem

Recipe sites are static pages. They do not know what is in your kitchen, they do not plan your week, and the shopping list is a separate chore.

I wanted to see how far one assistant could go when it sits across all of it: finding a recipe, recognising a dish from a photo, planning meals, and putting them on a calendar.

Constraints

  • Free-tier AI limits. Daily token budgets shaped which model does which job.
  • Voice and camera on the web. Input has to work in a browser without native apps.
  • Several kinds of user. Cooks, admins who curate, and a super admin who manages admins.
  • Solo build, over many months, alongside coursework.

Approach

The frontend is a Next.js dashboard with sections for ARIA, the meal planner, all recipes, my recipes, favorites, users, profile and security. The backend is an Express and Mongoose API with controllers for chat, recipes, meal plans, reviews and favorites.

ARIA is a LangChain agent. An AI-based intent detector decides what the user wants, a food-focus guard keeps it on cooking topics, and tools handle recipes and plans. Replies stream to the client as they are generated.

How an ARIA message is handled

Input type changes how a message arrives; everything after that shares one pipeline.

In the app

The landing page on a phone.

Key decisions

01Two model tiers, and fail fast on rate limits

Context
I started on Gemini in 2025. Free-tier rate limits and daily token budgets became the main constraint.
What I chose
The assistant now runs on Groq with two tiers: a primary model (llama-3.3-70b-versatile, about 100,000 tokens a day) for hard questions and a small fast one (llama-3.1-8b-instant, about 500,000 a day, five times the budget) for simple work. Retries are off, so a rate-limit error fails immediately and can be handled.
What I passed on
Stay on one model and retry on errors. That burns the same budget slowly and makes users wait.
Trade-off
Two models to tune, and the small one is weaker on nuanced questions.

02Voice runs in the browser, not on my server

Context
Speech-to-text on the server costs money and adds an upload step.
What I chose
The frontend uses the browser's Web Speech API with interim results, and the backend receives plain text marked as a voice message so it can reuse the normal chat pipeline.
What I passed on
Server-side transcription. A Google speech client is installed, but I did not use it for the live path.
Trade-off
Voice only works in browsers that support speech recognition.

03Photos become structured data

Context
A model describing a dish in free text is hard to use in the rest of the app.
What I chose
The image analyzer prompt asks for JSON (is it a dish, its name, ingredients and an estimated calorie count) and a culinary-focus guard rejects non-food images and off-topic requests.
What I passed on
Pass the photo straight into the chat and let the model improvise.
Trade-off
Less conversational, much easier to build features on.

04Meal plans keep snapshots of recipes

Context
A plan should not change if a recipe is edited or deleted later.
What I chose
Each planned meal stores a copy of the recipe details alongside its reference, and Google Calendar event ids are kept on the plan so events can be updated. The shopping list is computed by aggregating ingredients across the planned recipes.
What I passed on
Store only references and populate them on read. No duplication, but plans drift.
Trade-off
Duplicated data, accepted for stable plans.

05Three roles, enforced on the server

Context
Admins curate recipes and users; a super admin manages the admins.
What I chose
Middleware separates verified users, admins and super admins. The frontend also checks the auth cookie in a Next.js proxy before rendering protected routes, so unauthenticated users never see them.
What I passed on
Hide admin buttons in the UI only.
Trade-off
Every sensitive route needs the right middleware, which is easy to forget in a growing codebase.
From a weekly plan to your calendar

The browser's time zone decides when events land.

Challenges

  • Rate limits. Moving from Gemini to tiered Groq models was the biggest change, and the error handling had to be rewritten around fast failure.
  • Mobile layout. Modals and the calendar needed several rounds to feel right on a phone.
  • CORS. It started open to every origin and was later pinned to the production and local frontends.
  • A build failure from reading search parameters without a Suspense boundary, fixed late in the project.

Results

Recipia runs end to end: sign-up with roles, recipes with reviews and a rating distribution, favorites, a meal planner with a stats page, a shopping list, Google Calendar sync and ARIA with text, voice and photo input.

I do not have usage numbers for it, so I will not quote any. It is a project I used to learn how AI features, calendars and role-based access fit together.

What I would do next

  • Add real tests; there is one auth test file today.
  • Consolidate the older generations of meal-planner controllers.
  • Fix the typo in the fallback model name so the fallback path actually works.
  • Move calendar sync to a server-side refresh token so it does not depend on the client.

Contact

Exploring a new product idea? Looking for ways to improve your product? Send a mail to: