The most profitable question in African micro-commerce isn’t how to list a product — it’s what to stock this week. For co-sellers juggling thin margins and shifting demand, guesswork costs money. That’s why we built Kajota Pulse: a terminal-style dashboard that monitors live marketplace data, analyzes trends, and delivers one-click stocking decisions with AI-generated reasoning.
Kajota Pulse is the second app in a three-part stack — Coach drafts listings, Pulse decides inventory, and Mesh settles transactions on-chain. Built for the AWS × Vercel H0: Hack the Zero Stack hackathon, it runs entirely on serverless infrastructure: Vercel for compute, AWS Aurora Serverless v2 for data, and no managed servers or connection pools. Here’s how we pulled it off — and why three hidden engineering hurdles nearly derailed the project.
Building on the “zero stack”: no servers, no secrets, no passwords
The hackathon’s defining constraint was the zero stack: use Vercel for deployment, AWS for persistent state, and avoid managing any infrastructure. For a real-time analytics app, that meant relying on serverless compute and a managed database.
The architecture is intentionally minimal:
- Next.js 16 (App Router) on Vercel for the frontend and API routes
- AWS Aurora Serverless v2 (PostgreSQL) as the single source of truth
- Gemini 2.5 Flash for AI-powered stocking recommendations
- MongoDB Atlas Database Triggers streaming live data from the Kajota catalogue
Total footprint: five PostgreSQL tables, two SQL views, two Gemini endpoints, and one real-time ingestion API. No VPC configuration. No connection pooling. No password files.
But the most surprising outcome wasn’t the speed — it was the security.
Why passwordless auth isn’t just safer — it’s required
When we provisioned Aurora Serverless v2 using the new internet-access-gateway networking model, every password-based connection failed with PAM authentication failed. The model enforces IAM database authentication, which means no static passwords are allowed — and it also disables the RDS Data API.
Instead of storing a long-lived secret, every database connection uses a short-lived IAM authentication token generated at runtime:
const signer = new Signer({ hostname, port, username, region, credentials });
const pool = new Pool({
host,
port,
user,
database,
password: () => signer.getAuthToken(), // fresh token every time
ssl: { rejectUnauthorized: false }
});The pg library supports an async password callback, so the token refreshes seamlessly with each new connection. The result: no database password exists anywhere — not in environment variables, not in code, not in a secrets manager. Zero secrets, zero risk.
When AWS credentials aren’t yours: the Lambda shadowing trap
The IAM signer needs AWS credentials to mint tokens. We set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY in Vercel’s environment variables. That should have worked — except Vercel functions run on AWS Lambda, and Lambda injects its own execution-role credentials, overriding ours.
The signer was signing tokens with the wrong identity — one without the required rds-db:connect permission on the Aurora cluster. After an hour of debugging, we discovered the issue and switched to custom environment names:
function signerCredentials() {
const accessKeyId = process.env.PULSE_AWS_ACCESS_KEY_ID;
const secretAccessKey = process.env.PULSE_AWS_SECRET_ACCESS_KEY;
return accessKeyId && secretAccessKey ? { accessKeyId, secretAccessKey } : undefined;
}We created a dedicated IAM user with only rds-db:connect on the cluster’s database user, exposed under non-standard variable names. The Lambda shadowing problem vanished — and the passwordless connection remained secure.
Real data breaks assumptions: three bugs that seed data never shows
We wired Kajota Pulse to live MongoDB Atlas change streams on three collections: products, cosell_products, and orders. Each change event triggers a POST to /api/ingest, which upserts into Aurora. While this setup is fast and clean, real production data revealed three bugs that unit tests and seed files never catch.
- Extended JSON serialization. Atlas change events use EJSON, so a MongoDB
_idarrives as{"$oid":"abc123"}and a price as{"$numberInt":"9500"}. Without proper decoding, the dashboard rendersid="[object Object]"andprice=NaN. Two small utilities fixed it:
const ejsonId = (obj) => obj.$oid || obj;
const ejsonNum = (obj) => Number(obj.$numberInt || obj);- Collection name mismatches. The real collection is
cosell_products(with an underscore), but our API router was matchingcosellproducts. Events silently bypassed ingestion. We now normalize collection names before routing.
- Foreign key constraints under event disorder. Change streams don’t guarantee order. A co-seller listing can arrive before its referenced product. Our foreign key constraints rejected these rows. The fix: drop the FKs and treat each table as an independent projection.
These issues only surface under real load. That’s why we built the integration early — to catch problems before users did.
From dashboard to advisor: turning data into decisions
Dashboards show numbers. Advisors answer questions. Kajota Pulse was designed to do both — and to do it reliably, even when AI is unavailable.
The /api/recommend endpoint pulls live signals: trending demand, category margins, competitor stock-outs, and price positioning. It then sends this context to Gemini 2.5 Flash with a structured-output schema:
{
"product": "Organic Shea Butter",
"recommendation": "Stock 10–15 units before the weekend",
"reasoning": "+27 favorites this week, sits in the high-margin Beauty category (18%), and a competitor just ran out of a similar cream"
}Two design choices ensure robustness:
- Structured JSON output using
responseMimeType: "application/json"and aresponseSchemaguarantees clean parsing and rendering, not fragile prose extraction.
- A deterministic fallback. If Gemini is unreachable, a heuristic ranking (
demand × margin × opportunity) replaces AI decisions. The stocking card never shows an empty state — even during demos.
What zero stack buys: speed, safety, and zero ops
In five days, we built a production-ready analytics advisor that:
- Runs entirely serverless — no servers to patch, scale, or monitor
- Uses no long-lived secrets — passwords are impossible, and credentials rotate automatically
- Processes live data with minimal latency — change streams arrive in near real time
- Delivers AI-powered stocking advice that degrades gracefully
The zero stack didn’t just simplify deployment — it forced us to think about security, data integrity, and reliability in ways that traditional stacks often obscure. For teams building in fast-moving markets, that’s not just a hackathon trick. It’s a blueprint.
The next step? Bringing Pulse to real co-sellers and turning AI-driven stocking from a demo into a daily habit.
AI summary
Vercel ve AWS Aurora Serverless kullanarak sunucu yönetimi gerektirmeyen, sifresiz ve yapay zeka destekli bir gösterge paneli geliştirme rehberi. Kajota Pulse projesi detayları.