Disclaimer: this blog is not just for sports fans
The vast majority of the Super Bowl’s approximately 100 million annual viewers aren’t actually fans of the teams taking the field. They’re just people who are just looking for an excuse to get together with friends, eat healthy amounts of seriously unhealthy food, and watch commercials.
But if congeniality and carbohydrates aren’t enough to hold your interest, a little action can make the “Big Game” a bit more interesting.
Historically, most of the wagering on Super Bowl Sunday happens not at the big Vegas sportsbooks like Caesar’s or MGM, but instead in people’s homes, with friendly, informal wagers. The reigning champion of gambling games is a simple-to-learn, no-sports-knowledge-needed game commonly called “Super Bowl Squares.”
With many in-person party plans getting sidelined these past few years, we had an idea:
Create a “Roacher Bowl Squares” app to help distributed gamblers get their Big Game betting fix.
Don’t worry. This blog isn’t for sports fans or even gambling fans. In fact, before I started building this app, I couldn’t name more than two NFL teams (…there’s the Indianapolis Colts because I lived in Indianapolis and supporting them was a requirement. I might have shed a tear when Peyton Manning left. The other is the Patriots, because again, I lived in Indianapolis and we hate them).
This blog is about my experience building an interactive serverless application with Prisma, Remix, Netlify, Pusher Channels, and CockroachDB Serverless. Why did I choose Remix over Next.js? Was that even a good decision? Do I regret it? Read on dear reader….let’s find out.
It’s hip to be square: How this sample gambling app works
First, a quick README on how the game typically works:
A blank 10x10 grid is created. Typically on a sheet of paper.
The X and Y axes are labeled with the team names of the Super Bowl competitors.
Players claim squares from the blank grid at a preset dollar amount per square.
Once every box of the pool has been sold, the numbers 0-9 are randomly assigned to each row and column.
At the end of every quarter, the person whose square corresponds with the intersection of the second digit of each team’s score wins a prize (for example, a 14–7 score at the end of the first quarter pays out the owner of the square at column 4, row 7).
It’s conceptually pretty simple, but presents a handful of interesting challenges to solve when attempting to replicate this traditional, in-person version as a serverless application.
Below I’ll walk through some of the highlights, but if you’d prefer to just view the code, you can do that as well.
Remix vs Next.js: My Gambling App Tech Stack
Before getting into the complicated gymnastics of gambling application architecture let’s take a look at the players on the field (not sorry!). One of the first decisions I made was to use Remix instead of Next.js. I usually use Next.js for web applications but I decided to give Remix a try for two reasons: • It’s the new hotness and I love shiny things • The people behind Remix are amazing (Kent C. Dodds,
) I immediately regretted my decision. And I spent a few hours cursing into the ether because I put myself in position to learn a brand new framework from scratch in order to build a sample app in a matter of days. And then I started to love Remix. I could probably dedicate an entire blog post just fan boying Remix. In short I’ll just say I think it’s great that you can write an application using Remix and turn off JavaScript and it’ll still work. Just go try it already. The rest of my tech stack looks like this:CockroachDB Serverless
This application is far from stateless. We need a database to hold information about users, and the different games that are happening concurrently. CockroachDB Serverless is a great choice because of the nature of this application. A lot of the action takes place during The Big Game. Outside of that, the applications could be dormant only waking when participants are claiming their squares. This app takes advantage of CockroachDB Serverless’s elastic scale to provide compute when needed and be cost-effective when the demand recedes.
Prisma
Now that we have our database, we need a way to talk to it. Prisma is a typesafe ORM and my preferred way to work with CockroachDB. My dog and I actually appeared on the Prisma livestream to talk about a different sample app I built with the Prisma support for CockroachDB. I’ll link you straight to the good part (me):
Netlify
Remix can be deployed to many different platforms and lets you select which to use when starting a new project. I chose Netlify because I’m most familiar with it. During the build process, Remix will package up the static assets and create a Netlify function to handle the server-side rendering of your application. Don’t want to use Netlify? You’re just a small change away from using Vercel, Fly.io, and many others. I like Netlify. Sue me.
Pusher Channels
Just like with the Ugly Sweater app, we are using Pusher Channels to keep the game boards in sync while changes are being made. Before the game starts, this helps make sure no one tries to claim a square that was recently claimed by another participant. During the game, we get live updates of winning squares as the score changes.
Gambling application database schema
For the users:
id - UUID PRIMARY KEY DEFAULT gen_random_uuid()
username - STRING UNIQUE NOT NULL
password_hash STRING
created_at - TIMESTAMPTZ DEFAULT now()
updated_at - TIMESTAMPTZ DEFAULT now()
For the games:
id - UUID PRIMARY KEY DEFAULT gen_random_uuid()
slug - STRING (something human readable and unique)
host_id - UUID references to user table
state - STRING [INIT (default), Q1, Q2, Q3, Q4, FINAL]
claim_cost - DECIMAL (cost per square)
board - JSONB representation of the board Ex. {“teams”: [“Team 1”, “Team 2”], “rows” : [0, 1, 2, …, 9], “cols”: [0, 1, 2, …, 9]}
scores - JSONB
Ex. [[0,0], [0,0], [0,0], [0,0]]
winners - JSONB winners for all 4 quarters Ex. [{id: “”, name: “Jane Doe”, “userid”: “jd1”}, {id: “”, name: “Jane Doe”, “userid”: “jd1”}, {id: “”, name: “Jane Doe”, “userid”: “jd1”}, {id: “”, name: “Jane Doe”, “userid”: “jd1”}]
created_at - TIMESTAMPTZ
For the claims:
id - UUID PRIMARY KEY DEFAULT gen_random_uuid()
game_id - UUID
participant_id - UUID references to auth user table
row - INT
column - INT
created_at - TIMESTAMPTZ
The sample app is up, running, and waiting for you to host your own Roacher Bowl Squares game when the Big Game rolls around.
Try it out, share screenshots of your game, and let us know what you think by tagging #RoacherBowlSquares or
on Twitter.We have lots more sample apps in our Resources library as well. I hope you’ll check them out.