How to build a serverless polling application

Last edited on September 14, 2022

0 minute read

    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:

    1. The winner of our contest had never heard of an “ugly holiday sweater”. Apparently, in Great Britain, they are politely referred to as “holiday jumpers”.

    2. Pusher Channels make life easier and I highly recommend building with them.

    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.

    Why build a serverless application?Copy Icon

    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.

    Serverless Application Tech StackCopy Icon

    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:

    • To enter the contest: Text a picture and a name to the designated phone number for the “Ugly Sweater” contest.

    • To vote in the contest: Text the name of the person you want to vote for to the same designated phone number.

    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.

    Serverless App WorkflowCopy Icon


    The Netlify Platform: Using serverless functions as a webhook receiverCopy Icon

    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.

    The use of serverless functions in event driven architecture has become quite popular over the last few years and Netlify makes them easy to write and deploy. If you provide a JavaScript file with a handler function in a designated folder, Netlify will build, package, and deploy it as an AWS Lambda function.

    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.

    Twilio for inbound/outbound SMSCopy Icon

    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.

    CockroachDB Serverless for storageCopy Icon

    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 5 GiB and 50M Request Units), we can be sure you aren’t paying when no contests are active.

    Next.js for FrontendCopy Icon

    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.

    Pusher Channels as websockets for real time interactivityCopy Icon

    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 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.

    Serverless Application Database SchemaCopy Icon

    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:

    • id - UUID

    • name - String

    • description - String

    • max_votes - Int (Max number of votes per voter)

    For entries into the contest:

    • id - UUID

    • contest_id - UUID

    • name - String

    • picture - BYTES (1 MB max)

    • picture_type - String

    For voting in the contest:

    • contest_id

    • entry_id

    • voter_id (hashed from phone number?)

    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.

    how to build an application