CONTENTS

    Brandon Melville Built a Meal Planner That Prices Its Own Grocery List

    avatar
    Cici Yu
    ·September 19, 2026
    ·10 min read

    Brandon Melville, who teaches business owners how to fold AI tools into how they work, built CartCopia around a problem he framed simply: people starting out with fitness stall on meal planning because the plan, the budget, and whatever is already in the pantry never line up. The app takes a sentence — "for the next two weeks I'd like to try the Mediterranean diet" — and returns a fourteen-day dinner schedule plus a shopping list priced at the user's own grocery store. Momen is the backend; Claude Code built the React and Vite frontend on top of it.

    He didn't configure that backend by hand. Momen's no-code plugin connects Claude Code to a Momen project, so the tables, permissions, Actionflows, and API configs were created from a description — the setup Brandon demonstrates from scratch earlier in the video on a throwaway terminal to-do app, before opening up CartCopia. The install steps are in the Momen plus Claude Code setup guide.

    The finished build is public: open it in the Momen editor / Clone project, or try the deployed app at cartcopia-frontend.vercel.app.

    What CartCopia does

    • Saves a household's ZIP code, chosen Kroger store, size, cook nights per week, budget target, cuisine preferences, and excluded ingredients

    • Interprets a natural-language request into structured constraints before any recipe search runs

    • Builds a fourteen-day schedule where cook nights are spread out and the days between are filled as leftovers

    • Aggregates ingredients into one shopping list, minus anything already in the pantry

    • Prices each line against a real Kroger product at the chosen store, and leaves unmatched lines visibly unpriced

    • Puts a Stripe subscription in front of plan generation

    The data model: ten tables plus Momen's built-in account

    Momen ships an account table. The other ten were added for this app, and they split into household state, a shared recipe catalog, and per-plan output.

    Household state:

    • household_profile — zip_code, kroger_location_id and kroger_location_name (the store as both ID and label, so the interface can show it without a second API call), household_size, cook_nights_per_week, cuisine_preferences and excluded_ingredients as JSONB arrays, budget_target, allergies_acknowledged, is_subscribed, account_id

    • pantry_item — ingredient_name, status, account_id

    • household_product_preference — normalized_ingredient_name, kroger_product_id, product_name, account_id. This is the memory that keeps a household from re-picking the same brand of olive oil every two weeks

    The recipe catalog, shared across every account rather than owned by one:

    • recipe — themealdb_id, name, category, area, instructions, image_url, source_url. The themealdb_id is what deduplicates it: a recipe pulled once is reused instead of re-inserted

    • recipe_ingredient_line — raw_text, normalized_name, amount, unit, position, confidence, recipe_id. Recipe sites write ingredients as prose, so each line keeps the original text alongside the parsed quantity and a confidence value recording how well the parse went

    Per-plan output:

    • meal_plan — start_date, status, raw_request (the user's original sentence), interpreted_constraints, budget_target, account_id, plus household_size_snapshot and cook_nights_per_week_snapshot, so an old plan still reflects the household it was built for after the profile changes

    • meal_plan_entry — day_index, entry_type, servings_allocated, recipe_id, meal_plan_id, and source_cook_entry_id, a self-reference pointing a leftover day back at the night it was cooked

    • shopping_list — total_item_count, priced_item_count, combined_total, price_retrieved_at, meal_plan_id

    • shopping_list_item — the requirement and the matched product side by side: normalized_ingredient_name, required_quantity, required_unit, source_lines, is_owned, is_checked, then kroger_product_id, product_name, package_size, package_count, unit_price, line_total, is_variable_weight, confidence

    • subscription_order — amount, status, account_id

    Momen generates the relational structure and the GraphQL API from this, which is why the frontend needed no query code written for it. On getting this layer right first, see how to create data models for your app.

    Logins, and the columns a user is allowed to touch

    Momen's built-in authentication covers signup and login with no separate provider. Email, username, and phone number sign-in are all enabled; no single sign-on is configured.

    Permissions are set per role, per table, and per column, and the column layer is where this project gets specific. For the Logged-in User role:

    • Read access to household_profile, pantry_item, meal_plan, meal_plan_entry, shopping_list, shopping_list_item, household_product_preference, and subscription_order is filtered to the user's own rows

    • recipe and recipe_ingredient_line are readable in full, since the catalog is shared

    • On shopping_list_item, the role can update exactly one column: is_checked. Ticking items off in the store is a client action; prices, quantities, and product matches are not

    • On pantry_item, updates are limited to ingredient_name and status — enough to mark something in or out of stock, nothing more

    • household_product_preference is the one table the role can insert into, update, and delete freely within its own rows, which is what lets a household correct a bad product match

    That narrowing is what would normally mean hand-writing row-level security policies. Momen's permissions documentation covers how the role, table, and column layers compose.

    Six Actionflows carry the planning logic

    Ten Actionflows exist in the project. Four were generated by Momen's Stripe module; these six are the app.

    GenerateMealPlan — the entry point:

    • resolves the current account, queries that account's household_profile, then sets default outputs — a meal_plan_id of 0 and the message "An active subscription is required to generate a meal plan." — and branches on subscription status, so only the Subscribed path builds anything

    • maps requested cuisines onto TheMealDB's regional categories through a synonym table: Mediterranean resolves to Greek, Asian to Chinese, Latin to Mexican. With no cuisine given it falls back to American, Italian, Mexican, and Chinese

    • asks for twice the household's cook nights in candidates, clamped between 4 and 14, then interleaves and shuffles across regions so a two-cuisine request doesn't come back as one cuisine

    • parses each measurement against a whitelist of roughly sixty units — tbsp, cloves, pinch, handful — recording confidence as low when there's no number or the unit isn't recognized, rather than dropping the line

    • drops any recipe whose ingredients contain a listed exclusion entirely, rather than substituting

    • then lays out fourteen days: cook nights spaced evenly, every remaining day assigned as a leftover of the most recent cook night within four days. Each cook entry's servings_allocated is household size times one plus the number of leftover days it feeds, so the plan cooks bigger on the nights it needs to

    ApproveMealPlan — queries the plan by ID, account_id, and draft status in one go, so someone else's plan and an already-approved plan fail identically with "Meal plan not found, not owned by you, or already approved." It flips status to approved, then calls GenerateShoppingList as a sub-flow.

    GenerateShoppingList — the aggregation step:

    • reads the plan's cook entries and scales every ingredient line by servings_allocated / 4, since the source recipes are written for four

    • skips any ingredient matching a pantry item with status in_stock, in either direction, so "olive oil" in the pantry covers "extra virgin olive oil" in a recipe

    • sums quantities per normalized name, keeps every contributing raw_text in source_lines, and marks confidence low when two recipes disagree about the unit

    • pre-fills kroger_product_id and product_name from household_product_preference where a past choice exists

    PriceShoppingList — the part that makes the total real:

    • exchanges the Kroger client credentials for an access token, then searches products for each unpriced item, scoped to the household's kroger_location_id

    • scores every candidate rather than taking the first result: the ingredient's head noun must appear exactly or the candidate is rejected outright, word coverage drives the base score, an exact full-length match earns a bonus, each extra word in the product description costs a little, and words signalling the wrong kind of product — flavored, candy, soda, shampoo, candle — cost a lot

    • only writes a match scoring above a fixed floor of 50. Below it the line stays unpriced, which is the behavior Brandon points out on camera: when it can't find a price, it says so

    • aggregates the priced lines into priced_item_count, combined_total, and price_retrieved_at on the shopping_list

    ResolveKrogerLocations — takes a ZIP code and returns nearby stores, with distinct messages for a missing ZIP, missing credentials, and an unreachable directory.

    CreateSubscriptionOrder — inserts a subscription_order at amount 9 with status pending against the current account, ahead of the Stripe charge.

    All six are node graphs in Momen's Actionflow editor. Running them server-side is what keeps the pricing math and the ownership checks out of reach of the client — the argument for why backend structure matters even if you don't write code.

    One AI agent, and it is deliberately small

    The project has a single agent, MealPlanInterpreter, and it never touches recipes or prices. Its whole job is turning the user's sentence into structured fields before the deterministic code takes over.

    • Model — gemini-3.5-flash, at temperature 0

    • Inputs — raw_request, household_size, cook_nights_per_week, known_allergies

    • Output — structured, not prose: cuisine preferences in the user's own words, excluded ingredients (stated allergies merged with newly mentioned dislikes), a budget target only if a dollar figure was mentioned, a cook-night override only if the request gave a different number, and a short summary

    • Guardrails in the system prompt — never invent recipes, ingredients, or prices; never soften, remove, or reinterpret a stated allergy

    • No tools, no knowledge base, no database access

    That division is the decision worth copying: the model reads loose human phrasing, and everything with a consequence runs as code. Momen's AI integration makes structured output a configuration choice, so the flow downstream receives typed fields instead of parsing a paragraph.

    Where the outside data comes from

    Six external APIs, across two providers: TheMealDB's filter-by-area, filter-by-category, and lookup-by-ID endpoints build the recipe catalog, and Kroger's token, location search, and product search endpoints handle stores and prices.

    The Kroger client ID and secret are held as two project secrets rather than sitting in flow code, which is how ResolveKrogerLocations and PriceShoppingList share credentials without either containing them. That's Momen's secret management, and the calls themselves are configured API integrations rather than hand-rolled HTTP.

    Subscriptions, handled by Momen's Stripe module

    Payments are the one part of this backend Brandon didn't design. Enabling Stripe generated four Actionflows — one-time payment, recurring payment management, recurring deduction, and refund — plus the webhook endpoints Stripe posts back to. Setup was pasting the publishable and secret keys from the Stripe dashboard.

    The app's own contribution is one flow and one table: CreateSubscriptionOrder writes the pending row, Stripe takes the money, the callbacks update state. Momen's Stripe payment setup walks the same path, and putting a subscription in front of a generated deliverable is the pattern in the micro-SaaS launchpad.

    The frontend, and where it lives

    The interface is React and Vite, written by Claude Code and served from Vercel. Nothing about it lives in Momen — the project's web client holds a single empty page, because this build uses Momen purely as a backend. What Claude Code wrote against is the GraphQL API generated from the data model, plus the six Actionflows exposed as callable endpoints.

    What Brandon walks through on screen: an account tab with past orders and the subscription, a household settings screen, the request box that produces a two-week plan, an approve action that materializes the shopping list, a refresh-prices action that fills in the totals, and a pantry view — the thing that makes the shopping list shorter than the recipe list. Because the interface was built outside Momen it doesn't use Momen's one-click deploy, which is for frontends built in the Momen editor.

    What running this actually costs

    Building this project on Momen comes to $99/month on the Pro plan, which is also the plan Brandon confirms CartCopia runs on. The subscription is what decides the tier: paid subscriptions require Pro's payment capabilities, so charging for plan generation puts this above Basic regardless of how little data it stores.

    The useful part is that the $99 is the whole bill — every add-on line comes back at zero. Sized for 100 subscribers accumulating over 225 equivalent operating days, the project needs about 30 MB of database storage against Pro's 1 GB, 635K AI points a month against Pro's 5 million, and 0.03 requests per second of peak load against Pro's 25. No object storage, no outbound transfer. Nothing here is close to a ceiling, which is what happens when the metered resources are text and numbers and the AI agent only reads one sentence per plan. You can estimate the cost of your own project using Momen's pricing calculator.

    Brandon walks through all of it — the plugin install, the terminal to-do app, then CartCopia's data model, APIs, Actionflows, agent, payment and permission settings, and the server sizing on both Free and Pro — in his video. The project is open in the Momen editor, and the deployed app is at cartcopia-frontend.vercel.app.

    To build along these lines: install the Momen plugin in Claude Code, let it build the backend before any interface exists, keep the AI agent narrow enough that it only interprets input, and put every calculation a user could dispute into an Actionflow rather than the browser. Momen calls this shape vibe no-coding — pointing the coding agent at a configured backend instead of asking it to invent one.

    Vibe No-Coding with Momen Today. Describe Your App, Own Every Piece AI Builds.