Skip to content
Nina Rhone

Selected work

myplantry

Photograph your receipt, and get recipes for what is about to expire.

Try itIn beta2026

The myplantry recipes screen, with a prompt box for nudging the recipe and a list of ingredients that expire soon

The problem

I kept buying food I already had and throwing out food I forgot about. Deciding what to cook meant opening the fridge and guessing. I wanted an app that knew what was in my kitchen and what needed using first.

What it does

myplantry keeps an inventory of your kitchen and tracks when each item will go bad. It suggests recipes built from what you have, starting with the items closest to expiring. You can also ask Kitchen AI, a cooking assistant, questions while you cook, and build a grocery list.

Getting food in

There are four ways to add food, and three of them are a photo.

  • Receipt photo. Gemini reads the line items and decodes abbreviations like “CHKN BRST”.
  • Fridge or pantry photo. Gemini identifies the items it can see.
  • Handwritten list. A photo of the list is transcribed into inventory.
  • Manual entry.

Recipes can be imported from a URL or from pasted text.

Knowing what expires

  1. Each item you type in or buy from your grocery list is fuzzy-matched with pg_trgm against a 433-row table seeded from USDA FoodKeeper data.
  2. If nothing matches, Gemini estimates the shelf life.
  3. That estimate is saved back to the table, so the next person who adds the same food gets a plain lookup with no model call.

The date also depends on where the food is kept. The same item gets a different shelf life in the fridge, the pantry or the freezer, and the date is recalculated when you mark it as opened.

Photo scans are the exception. The vision model estimates shelf life in the same call that reads the photo.

Suggesting recipes

Recipe generation is one Gemini call with no agent loop. The prompt carries the full pantry with expiration dates, the user’s dietary and cuisine preferences, and the titles of their last 10 saved recipes so suggestions do not repeat. Guardrails in the prompt force a recognizable dish and block invented combinations such as vinegar pasta. The prompt asks for raw JSON, and the reply is cleaned and parsed before anything is shown.

Keeping AI cost under control

Every AI feature runs on my Gemini budget, so each one has a per-user daily cap, and each user has a per-minute limit across all of them. The check and the usage record happen in one Postgres function behind a per-user lock, so parallel requests cannot slip past the cap. I get an alert when one account passes 30 AI calls in an hour.

When the model fails

  • Shelf life. The model fallback times out after 10 seconds, and the item gets a default date instead of none.
  • Cooking assistant. A failed call returns a plain “try again in a moment” message.
  • Parsing. Replies are stripped of code fences and parsed defensively, so a malformed reply gives an empty result, not a crash.

Data and sign-in

Next.js App Router route handlers serve the API on top of Supabase Postgres. Tables use row-level security, and every API route checks the session on the server. NextAuth handles Google sign-in and email and password.

Tests

The repo has 11 test files. They cover pantry state, saved recipes, the pantry and recipe API routes, sign-up and login, and AI response parsing. GitHub Actions runs the type-check, lint, tests and a production build on pushes and pull requests to the main branches.

What I learned and had to decide

  • Steering the recipe. The first version had one button that generated a recipe from the pantry. Sometimes I wanted a particular kind of dish and had no way to say so. I added an optional text box for a nudge like “something cozy” or “30-minute meal”. The prompt treats it as guidance and still favors what expires soonest.
  • Using part of something. Testing with my aunt raised a question I had not thought about: if you have three bags of rice and have used half of one, how do you say that? Quantity can now be a fraction. A “Used how much?” menu takes 25, 50, 75 or 100 percent of one unit, or a custom amount, so three bags become 2.5. Finishing a unit moves on to the next one with a fresh date.
  • Adding a grocery list. Updating the pantry is the moment you notice what you are missing, so the grocery list belongs right next to it. Marking an item as bought moves it into the pantry with an expiry date.
  • Drawing the food myself. The food emojis on the landing page are ones I drew by hand on my iPad, in place of a stock icon set.

Stack

Next.js, Supabase Postgres (pg_trgm), Gemini (vision and text), NextAuth, Vercel, Vitest, GitHub Actions.