AWS Builder Center
Why I chose DynamoDB for a Global Gotham Community racer | H0 Hackathon

Why I chose DynamoDB for a Global Gotham Community racer | H0 Hackathon

Gotham Knights: building real leaderboards with DynamoDB on Vercel | #H0Hackathon

#H0Hackathon

The problem

I wanted a competitive game for an initial group of 39 friends — no accounts, no app store, no “sign up to see the scoreboard.” Just pick your operative, race, trash-talk in the group chat, and climb per-track leaderboards. Yes more would like to join the game from around the world.
That’s Gotham Knights: a neon physics Batmobile racer through Gotham districts, built for the H0 hackathon (Vercel v0 + AWS Databases).
Demo video:

Go Faster


Why this problem?

Most weekend prototypes treat the database as an afterthought. I wanted the opposite: an initial small social game (which could expand to millions of gamers) where the data model is the product — leaderboards, personal bests, and run history that still make sense if the group grows.
Track 3 (Million-Scale / Gaming) pushed me to ask: if 39 friends became 39,000 players, would this architecture still hold? DynamoDB gave me a clear yes for the read patterns I actually need.

Why DynamoDB (and not Aurora)

I considered Aurora, but this app’s access patterns are classic key-value / wide-column work:
Access patternQuery
Top scores on a trackAll rows under LEADERBOARD#trackId
Player’s best per trackRows under PLAYER#slug where SK begins with BEST#
Full run historyRows under PLAYER#slug where SK begins with RUN#
No joins. No migrations mid-hackathon. Single-table design on one partition key + sort key:
  • PLAYER#slug / RUN#timestamp#runId — append every run
  • PLAYER#slug / BEST#trackId — personal best per district
  • LEADERBOARD#trackId / PLAYER#slug — ranked row for the board
When you finish a run, the API writes the run, checks your best, and updates the leaderboard row only if you improved — three targeted writes, no SQL ceremony.
Leaderboard reads are a single Query on LEADERBOARD#trackId, sorted in app code. That’s O(players on that track), which is exactly what a game leaderboard needs.

Vercel + AWS without long-lived keys

Frontend and API live on Vercel (Next.js App Router). DynamoDB is wired through the Vercel ↔ AWS Marketplace integration.
Credentials use Vercel OIDC → awsCredentialsProvider from @vercel/functions/oidc — no hardcoded AWS access keys in the repo or env files that rotate manually. The serverless API routes assume a role and talk to DynamoDB per request.
That’s the “zero stack” story the hackathon is about: v0 scaffolded the UI fast; I swapped in a custom Canvas physics engine; the data layer is production-grade from day one.

What I’d do differently at scale

  • GSI or sparse index if I add cross-track “global champion” views
  • DynamoDB Streams + Lambda for async fan-out (badges, rival notifications)
  • TTL on old RUN# items if history grows faster than storage budget
For a friends-first v1, partition-by-track and partition-by-player was the right tradeoff.

Stack summary

  • v0 + Next.js 16 — UI, routing, API routes
  • HTML Canvas — Stonkrider-style physics (custom engine)
  • Amazon DynamoDB — runs, bests, leaderboards (single table)
  • Vercel deploy + OIDC — live at the URL above
If you’re building something social with simple read patterns, DynamoDB on Vercel is a surprisingly good hackathon default — as long as you design the keys around how people actually query the data, not around how the ORM wants to draw tables.
Happy to answer questions in the comments.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article