
Parali Setu: building a stubble marketplace on AWS in four days
How we built a serverless marketplace in ap-south-1 that lets farmers sell crop stubble instead of burning it, and what tripped us up with Amplify, Cognito, API Gateway and CloudFront.
# Parali Setu: building a stubble marketplace on AWS in four days
Every October and November, and again in April and May, farmers in Punjab and Haryana burn the stubble left after harvest. They mostly don't want to. Clearing a field costs money, and the next crop has to go in within weeks. By our estimate, from satellite-mapped burned area, about 70 lakh tonnes of paddy straw burns every year. On the worst November days, stubble smoke has made up to 37% of Delhi's PM2.5.
The frustrating part is that the straw has buyers. Biomass and bio-CNG plants, paper mills and cattle feed makers all want it. What's missing is a way for a farmer and a buyer to find each other at a fair price, and someone to haul it. We built that for the WeMakeDevs x AWS Environmental Hacks and called it Parali Setu, "stubble bridge".
What it does
There are four roles.
- A farmer lists stubble in four short steps and sees a fair price band for their region.
- A buyer sees one delivered price per tonne (stubble plus transport), makes an offer, and the farmer can accept or counter once.
- A transporter picks pickup jobs from a board and earns the haulage fee.
- An admin approves listings and verifies people.
Every delivered tonne adds to a public counter on the home page: tonnes kept out of the fire, CO2 and PM2.5 avoided, and rupees paid to farmers. It works for every Indian state in seven languages (English, Hindi, Punjabi, Haryanvi, Tamil, Telugu and Malayalam), and it's built for cheap phones, with short paged steps instead of long scrolling.
The architecture
Everything runs in ap-south-1 and is written in
Amplify Hosting serves a static Next.js site. Requests to /api go through API Gateway to one Lambda function per route (Node.js on arm64). Cognito handles accounts, DynamoDB holds everything, S3 holds photos, SES sends order emails, and Bedrock, Polly and Translate power the assistant, the read-aloud and the translations.
A few decisions mattered more than the rest.
One DynamoDB table, with real guarantees. Listings, offers, orders and the impact counter share a single table. The risky moments are the ones where money or ownership changes hands: accepting an offer, claiming a delivery job, paying. Each uses a conditional write or a transaction. To check it, we fired the same accept, claim and pay request several times at once. Exactly one succeeded each time, and the impact counter went up once.
The buyer pays exactly what they were shown. The delivered price is calculated when the offer is made and saved on the offer. The first version recalculated it when the order was created, and a buyer who had changed their profile in between could be charged a different total. We found that in a review and fixed it, and now there's a test for it.
Login in cookies, not in the browser. A static site has nowhere to hide a token, so we moved login into our own Lambdas. They call Cognito and set httpOnly cookies, and the page's JavaScript never sees a token. State-changing requests need a custom header against CSRF. Admin accounts need an authenticator-app code every time and can't be created from the public signup.
Caps on anything that costs money. Every route has a per-user rate limit stored in DynamoDB with TTL. The AI assistant is also limited to 100 calls a day across all users, so a bad day can't produce a bad bill.
What tripped us up
Our own mistakes taught us the most, but these AWS details cost us real time.
- Amplify and cookies. Amplify can proxy /api to another origin, so our cookies are first-party. But rewrites don't forward cookies under the default cache setting, AMPLIFY_MANAGED_NO_COOKIES, so login worked locally and silently failed live. Switching to AMPLIFY_MANAGED fixed it. Next.js 16 also needs the app set to the static WEB platform, not WEB_COMPUTE, or every page returns a 404.
- CORS on API Gateway. With credentials on, a wildcard method list and the capacitor://localhost origin are rejected with a generic "Invalid format for origin" error, which rolled the stack back.
- Throttling that did nothing. Stage route settings are passed through as raw JSON, so our lowercase property names were ignored. The throttling on the AI routes only worked once we used PascalCase.
- CloudFront on a new account. A new account can't create a distribution until it's verified, which failed our first deploy and made us turn photo upload off.
- Bedrock in Mumbai. We had to list inference profiles from the CLI to find one that works in ap-south-1.
How we know it works
We wrote a script, verify-deploy, that checks the live site end to end. It confirms the stacks are healthy, the API answers through the Amplify proxy, a test user can log in and gets httpOnly cookies, a write really lands in DynamoDB, the security headers are set and no source maps are served. It prints a pass or fail table, and it runs against the real stack, so a green result means the real thing works.
How we built it
We used Claude Code as an AI coding assistant throughout, and a substantial part of the code was generated with it under our direction. We decided what to build and the rules it follows, reviewed and tested what came back, found the problems (a layout that jumped, a login that failed behind the proxy, translations where machine translation turned "stubble" into the word for "eggs" in Tamil) and handled the AWS setup ourselves. The translation bug is a good reminder that Amazon Translate gave us a first pass, and people still need to read it.
What's next
Payments are a labelled demo for now, and Razorpay is the next step. After that comes phone OTP login, which needs SMS registration in India, and getting SES out of the sandbox so every user gets order emails.
Code: https://github.com/Bhavarth257/haybales\_ecommerce
=====
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article