{"slug": "i-built-a-global-opinion-platform-in-72-hours-here-is-what-actually-went-wrong", "title": "I Built a Global Opinion Platform in 72 Hours — Here Is What Actually Went Wrong", "summary": "A developer built Groundswell, a global daily opinion platform, in 72 hours using Next.js, Aurora DSQL, DynamoDB, and Amazon Bedrock. The project encountered several critical mistakes, including assuming Aurora DSQL works like standard PostgreSQL, planning for DynamoDB tables that couldn't be created due to IAM restrictions, and underestimating the differences between concurrent writes and relational reads. The developer shares lessons learned from debugging these issues during the time-constrained hackathon.", "body_md": "I finished the Groundswell hackathon build with a working product, a deployed URL, and a list of things I wish I had known before I started. This is that list. Not the polished version where everything went according to plan — the actual version, including the three hours I spent debugging a database connection that was failing for a reason I had completely wrong.\n\nGroundswell is a global daily opinion platform where players vote on a question and then predict how different countries and age groups voted. Your score comes from prediction accuracy, not opinion alignment. Building it in 72 hours using Next.js, Aurora DSQL, DynamoDB, and Amazon Bedrock taught me more about AWS database architecture than I expected. Most of what I learned came from mistakes.\n\nMistake 1 — Assuming Aurora DSQL Works Like Regular PostgreSQL\n\nMy mental model going in was that Aurora DSQL was PostgreSQL with a fancier name and better global distribution. It is not. It has a PostgreSQL-compatible connection interface, which means your queries look the same, but several things work differently and some features that standard PostgreSQL has are missing entirely.\n\nThe first problem was authentication. Aurora DSQL does not accept a static database password. Connection requires a short-lived token generated from your AWS credentials each time you connect. When running locally, this token comes from the Vercel CLI when you run the app with vercel dev. If you run the standard npm run dev command instead, the token is never injected, and every database connection attempt returns an access denied error. I spent several hours convinced my environment variables were wrong before realizing the issue was which command I used to start the server.\n\nThe second problem was schema creation. Foreign key constraints are not supported in Aurora DSQL. I had to remove every REFERENCES constraint from my table definitions and handle referential integrity in the application layer instead. Index creation requires an ASYNC keyword that standard PostgreSQL does not use. Descending sort orders on indexes are not supported. Each of these discoveries happened at the moment of running the migration, not before.\n\nThe fixes were all straightforward once I understood what the actual limitations were. But discovering them during a time-constrained build was expensive. If you are building with Aurora DSQL, read the limitations section of the documentation before you write your schema.\n\nMistake 2 — Planning for Tables That Did Not Exist Yet\n\nThe original DynamoDB design for Groundswell had two separate tables. One for individual vote records to prevent duplicate submissions. One for demographic segment counters to track yes and no counts by country and age group. The separation felt clean on paper.\n\nWhat I did not know until I tried to create those tables programmatically is that the IAM role provisioned by the Vercel DynamoDB Marketplace integration is scoped specifically to the single table that Vercel creates automatically. Attempts to create additional tables returned access denied errors. The permissions simply did not extend beyond the auto-provisioned table.\n\nThe solution was to redesign the schema as a single-table design on the Vercel-managed table, using partition key prefixes to distinguish between item types. Vote records use a VOTE prefix. Segment counters use a COUNTER prefix. Both live in the same table and are accessed through different query patterns that never conflict.\n\nWhat felt like a constraint turned into the correct architectural decision. Single-table design is the pattern AWS recommends for DynamoDB when access patterns are well-understood. The constraint removed an option I should not have been using anyway and pushed me toward the better solution.\n\nMistake 3 — Underestimating How Different Concurrent Writes and Relational Reads Actually Are\n\nGoing into the build I knew I needed a database for votes and a database for user profiles and leaderboards. What I underestimated was how incompatible those two requirements actually are at the level of database internals.\n\nThe vote ingestion pattern is extremely write-heavy with high concurrency. Thousands of players potentially updating the same demographic counter rows at the same time. DynamoDB's atomic ADD operation handles this natively — every increment happens correctly without any locking or transaction coordination. The same operation on a relational database under the same load would produce lock contention that grows with concurrency.\n\nThe leaderboard and scoring patterns require SQL. The global ranking uses a window function that computes each player's position relative to all other players in a single query. The prediction scoring joins two tables to compute error points for every player in one operation. These are not operations you can replicate in DynamoDB without significant application-layer workarounds that become slower as the data grows.\n\nOnce I understood those requirements clearly, the database separation became obvious rather than optional. DynamoDB for the writes. Aurora DSQL for the reads. A nightly pipeline that moves data from one to the other. The architecture that emerged from following the access patterns strictly is the one I would design the same way from the start if I were doing it again.\n\nWhat Actually Surprised Me in a Good Way\n\nThe Bedrock integration for question generation was simpler than I expected. A prompt describing the type of question I needed, a structured JSON format for the response, and some string parsing to remove the markdown wrappers the model adds around its output. The model returns five candidate questions with predicted divergence scores. An admin reviews and approves one before it goes live. The whole thing works reliably.\n\nThe Vercel Marketplace provisioning for both databases genuinely took minutes. Environment variables were injected automatically. The infrastructure was ready before I finished reading the documentation.\n\nI created this content as part of entering the H0: Hack the Zero Stack hackathon. The mistakes were expensive in time during the build. They were worth it for what they taught.\n\ntags: aws, dynamodb, nextjs, hackathon, webdev", "url": "https://wpnews.pro/news/i-built-a-global-opinion-platform-in-72-hours-here-is-what-actually-went-wrong", "canonical_source": "https://dev.to/hafiz_muhammadsalman_f28/i-built-a-global-opinion-platform-in-72-hours-here-is-what-actually-went-wrong-40b6", "published_at": "2026-06-29 22:22:09+00:00", "updated_at": "2026-06-29 22:48:50.886797+00:00", "lang": "en", "topics": ["developer-tools", "ai-infrastructure", "large-language-models"], "entities": ["Groundswell", "Next.js", "Aurora DSQL", "DynamoDB", "Amazon Bedrock", "AWS", "Vercel", "IAM"], "alternates": {"html": "https://wpnews.pro/news/i-built-a-global-opinion-platform-in-72-hours-here-is-what-actually-went-wrong", "markdown": "https://wpnews.pro/news/i-built-a-global-opinion-platform-in-72-hours-here-is-what-actually-went-wrong.md", "text": "https://wpnews.pro/news/i-built-a-global-opinion-platform-in-72-hours-here-is-what-actually-went-wrong.txt", "jsonld": "https://wpnews.pro/news/i-built-a-global-opinion-platform-in-72-hours-here-is-what-actually-went-wrong.jsonld"}}