Very few traditions can melt the corporate ice as well as an ugly holiday sweater contest. Which is why, this past December, I took on the challenge of building an Ugly Sweater Voting Application to entertain our 100% distributed team.
The application (which can be used to vote on any images - not just ugly sweaters) consists of a dashboard that displays the status of the contest and a serverless backend that facilitates bootstrapping the dashboard and handling updates from SMS messages using websockets. The results of the contest were…ugly! (Aside from our CEO, of course, who somehow wore an ugly sweater but made it fashion.)
There were two interesting takeaways from the Ugly Sweater App experience:
This is one of the first sample apps I’ve built with CockroachDB, and I was pleased with how well the database fit with a lot of my preferred tools. The github repo is here if you want to jump to the README.
For me, operations do not spark joy. My guess is that you don’t love them either. And, at this point, the tooling that supports serverless application development has matured enough for everyone to feel confident using it. of course, there will always be your favorite tools that you’d rather not leave behind. The database is a good example of this. If you’re still exclusively using Postgres or MySQL, I see you. I understand. But there are serverless alternatives. The same holds true up and down the stack.
In short, you should build serverless applications if you want to build fast, focus on application code, and have an application that can scale automatically if need be.
The goal of this Ugly Sweater app was to produce an interactive experience for my team, while also demonstrating multiple common ways of accessing data using CockroachDB. Here’s how the app works:
The functionality of the application is simple by design. It’s the components of the application that I think are important and are worth further explanation. Take a look at the diagram and then I’ll explain the application component functions and why I chose them.
It’s no secret that I’m a fan of Netlify (just look at any of my projects from the last few years) and this application takes full advantage of their platform. Communication between Twilio and the application is handled using Netlify functions while the dashboard is a Next.js application deployed using the Netlify Essential Next.js Build Plugin.
Serverless functions were a great fit for this application because we needed an endpoint to act as a webhook receiver. This was an endpoint Twilio could send a POST request to upon receiving an MMS message to your Twilio phone number. It was important that this function could scale to handle the load if many people were entering and voting at once because this function is responsible for all the actions of the contest. Those actions include stuff like creating a new entry or updating the vote count. This was accomplished by writing to CockroachDB and pushing new events into Pusher Channels.
I wanted this app to have a very low barrier to entry. Anyone should be able to participate without needing to install an app. I think it’s safe to say that in 2022, most everyone has a device capable of sending MMS. Enter Twilio. Twilio has been around a long time and has been my go-to for creating fun interactive applications. While most of us have probably used Twilio to send a text message, not everyone has used Twilio to receive text messages (along with any images that were sent). This works well because sending an image also reduces the image file to less than 1MB which is small enough to store in a database BYTES column. This allows us to treat messages with an image as an entry and messages without an image as votes. Once we configure the Messaging Webhook to our Twilio Phone number to POST to our serverless function, each message (along with any images) will be sent to our application to process.
Once our serverless function has finished processing the text messages from Twilio, the data needs to be stored so it can be displayed to the participants in the dashboard. We’ll need to keep track of all the contests that could be happening concurrently along with their entries which include images and votes.
Since we’re using a serverless function that scales to handle increased load, we need a database that can do the same. This makes CockroachDB serverless a great option. You can start using CockroachDB serverless for free. This particular app takes advantage of the BYTES column type which allows us to store the image in the database. We also use foreign keys and unique constraints to ensure data integrity. And because CockroachDB serverless uses consumption based pricing (free up to 5GB and 250M Request Units), we can be sure you aren’t paying when no contests are active.
Now that contest entries and votes are being written and updated in CockroachDB, we need a way for the participants to view them. I could have used any framework to build this dashboard. A lot of my projects use Next.js, so I used it for this one as well. If I were to do it again, maybe I’d use something like Remix… (possibly foreshadowing my next blog?). Next.js allowed me to render the pages on the server, pulling the data from CockroachDB using Prisma.
Prisma is an ORM that helps developers quickly build applications using CockroachDB. CockroachDB and Prisma are in the early stages of what will eventually become a full scale integration.
Displaying contest entries and vote tallies is great, but because participants are all over the world (US, UK, Italy, India…) we need to be able to see the votes appear in real-time. No one has time to sit at a website hitting F5. This is where Pusher comes in. Their Channels product is websockets as a service which pairs well with our serverless architecture. Instead of spinning up your own socket.io service, you can just create a new Channels app and connect to it. From the serverless function, you can push events into your channel and receive them on any frontend subscribed to the channel in real time. Each contest gets its own channel and the dashboard listens for any new votes or entries and updates accordingly.
If you want to learn more about writing schema for a distributed SQL database like CockroachDB I recommend taking an online course or reading through some of our blogs and documentation. (Yes, it’s important to write schema! It will save you headaches down the road.)
Here is the database schema that I wrote for this application (you can view all the SQL Statements here.):
For the contest:
For entries into the contest:
For voting in the contest:
That’s it. That’s the app. If you have questions about any of the components I chose or how I used them just send me a note on Twitter. I also do a weekly livestream on twitch where you can drop in and ask me questions while my dog makes charming noises in the background.
In this tutorial, we’ll create an app for coordinating social events. In the process, you’ll see how simple it can …Read More
To help people get outdoors and improve their physical well-being, we’ll create an outdoor activity tracker. Although we …Read More