AWS Builder Center ran a Weekend Challenge over 14–17 August 2026: build a simple app that makes something creative — words, images, sound, or play — deploy it on at least one AWS service, and write it up. The first 50 qualifying submissions get an AWS Builder Jacket. Three days, one weekend, one small idea.
I built Fridge Roulette. It's live at
fridge.balakrishnan.me,
the source is on GitHub,
and the challenge writeup is
published on Builder Center.
This post is the version with the parts I found genuinely interesting — mostly the
two things about Amazon Bedrock in ap-south-1 that would have cost me
the weekend if I'd found them later.
The idea
Every evening has the same small problem in it. You open the fridge, find half a cabbage, some paneer and two tomatoes, and lose ten minutes deciding what to do with them. Recipe sites are no help, because they work the wrong way round — they tell you what to buy. I wanted something that starts from what is already in front of you.
So: type your leftovers in plain words, pick how much time you have and whether you want it vegetarian, and get back a complete dish — a name the model made up, four to six steps, cook time, servings, an honest list of anything you're missing, and swaps for what you don't have.
Nothing is looked up. There's no recipe database behind this and no table of ingredient combinations — every dish is invented on the spot, right down to naming it. Ask the same question twice and you get two different dinners. That's the part that made it fit a creative challenge rather than a CRUD app.
Three details make it feel like a tool rather than a demo. The time budget genuinely changes the recipe rather than just labelling it — ten minutes gets you assembly, forty gets you something simmered. Use-it-up mode tells it to prioritise whatever spoils first, which is the actual reason most of us are staring into the fridge. And the "missing" list is honest: if the dish really needs one thing you didn't mention, it says so instead of silently pretending you own it.
Architecture
Deliberately small. The whole backend is one Python file with no dependencies beyond
the boto3 already in the Lambda runtime, and the frontend is one HTML
file with inline CSS and JavaScript. No build step, no bundler, no framework. For an
app whose entire job is "take a sentence, return a recipe", anything more would have
been ceremony.
Browser ──HTTPS──> CloudFront ──> S3 (static site)
│
└──POST──> API Gateway (HTTP API) ──> Lambda (Python 3.13) ──> Amazon Bedrock
Nova Lite
- Amazon Bedrock — Nova Lite invents the dish, returned as structured JSON.
- AWS Lambda — builds the prompt, calls Bedrock, parses and validates the response.
- Amazon API Gateway — HTTP API with CORS, and the throttle that caps my spend.
- Amazon S3 — hosts the static frontend.
- Amazon CloudFront — HTTPS and the custom domain.
Serverless because the traffic shape demands it. This app is idle almost all the time and then busy for a few seconds at dinner time. Nothing to keep warm, no capacity to plan, nothing to pay for between requests.
Why Nova Lite and not something bigger
The workload is tiny and structured — about 200 tokens in and 170 out per recipe. Nova Lite is the cheapest model in the family that reliably holds a JSON shape at that size, which works out to roughly $0.05 per 1,000 recipes. Nova Pro costs around thirteen times as much and would not invent a better stir-fry from half a cabbage.
Picking the smallest model that clears the bar is most of cost control on Bedrock. The instinct to reach for the biggest model available is expensive and, for a task like this, buys nothing.
The gotcha that eats weekends: inference profiles
This is the one I'd want to know before starting. In ap-south-1, every
Nova model is available only through a cross-region inference profile.
Calling the bare model id is rejected outright:
# rejected in ap-south-1
amazon.nova-lite-v1:0
# what you actually need
apac.amazon.nova-lite-v1:0
You can confirm it for your own region in one command — anything listed as
INFERENCE_PROFILE cannot be called by its plain model id:
aws bedrock list-foundation-models --region ap-south-1 \
--query "modelSummaries[?contains(modelId,'nova')].[modelId,inferenceTypesSupported[0]]" \
--output text
The failure mode is unhelpful. You get a permissions-flavoured error rather than anything pointing at inference profiles, which sends you off auditing IAM instead of fixing the model id. I saw at least two other people in the challenge thread hit a wall here — one of them abandoned Bedrock entirely and wrote their generator by hand rather than fight it.
Cross-region inference changes what IAM needs
Follows directly from the above, and it's the second place to lose an hour. Because the call may actually execute in a different region, permission on the inference profile alone is not enough. You need both the profile ARN and the underlying foundation model ARN, with a wildcard region on the latter:
{
"Effect": "Allow",
"Action": "bedrock:InvokeModel",
"Resource": [
"arn:aws:bedrock:ap-south-1:<account-id>:inference-profile/apac.amazon.nova-lite-v1:0",
"arn:aws:bedrock:*::foundation-model/amazon.nova-lite-v1:0"
]
}
Trusting what the model gives back
The real engineering problem wasn't calling the model. It was trusting the response.
A model asked to produce JSON will comply almost every time, and then occasionally
wrap it in a code fence, add a cheerful sentence before it, or return
time_mins as the string "20 minutes" instead of a number.
Any one of those breaks a UI that assumed it could just render the response. Three
layers deal with it:
- Show, don't tell. The prompt carries one worked example of the exact shape I want, every field filled in. Showing the model the target is far more reliable than describing it.
- Parse defensively. Take everything between the first
{and the last}rather than trusting the whole response to be clean JSON. Stray prose and code fences stop mattering. - Never render raw model output. A
_cleanfunction coerces every field to the type the frontend expects — lists stay lists, numbers that arrive as text fall back to defaults, a missing dish name becomes "Fridge Surprise". The only genuinely fatal case is a recipe with no steps.
The result is that a slightly misbehaving model produces a slightly plainer recipe instead of a broken page. Prompt engineering here was mostly schema engineering — I spent more time on the shape of the response than on the wording of the request.
Know your real ceiling
I set an API Gateway throttle at 5 requests per second and assumed that was my limit. It wasn't. The Nova Lite cross-region quota on my account is 40 requests per minute — roughly 0.67 per second, about seven times tighter than the throttle sitting above it.
| Layer | Limit |
|---|---|
| Bedrock Nova Lite | 40 requests/minute — the real ceiling |
| API Gateway | 5 req/sec, burst 10 |
| Lambda | 10 concurrent executions (account default) |
The binding constraint in a serverless stack is rarely the layer you configured
yourself. Worth finding out before your users do — and worth handling gracefully.
Bedrock throttling is a ClientError, so it's caught and surfaced as
"The kitchen is busy, try again in a moment" rather than a stack trace.
Two small things that cost real time
The Lambda default timeout of three seconds is not enough for a Bedrock round trip, which takes one to four. Raise it to 30 or you'll chase phantom failures that are really just the function being killed mid-call.
And validate every input before it reaches the model. An empty fridge, a 500-character essay, or a nonsense time budget gets rejected or defaulted at the edge. Validating early keeps junk from ever becoming a billable model call — a habit worth having anywhere you're paying per request.
What it cost
Effectively nothing. At roughly $0.05 per 1,000 recipes, the entire challenge weekend — building, testing, and everyone who has tried it since — came in under a rupee or two of Bedrock usage. S3, CloudFront and API Gateway at this volume round to zero.
The guardrails matter more than the bill, though. A public endpoint that calls a paid model on every request is exactly how people wake up to a surprise invoice, so the API Gateway throttle and a budget alarm went in before the app went public, not after.
Try it
fridge.balakrishnan.me — no sign-up, nothing to install. The page loads with an example fridge already filled in, and there are one-click sample fridges under the box if you'd rather not type. Source is on GitHub.