< Back to portfolio

Voting App

Tech stack: Node, Express, MongoDB

A web voting app I created with Node.js, ExpressJS, MongoDB and basic front end. A lot went into it to make it as robust and secure as possible and also cover the necessary features and edge cases.

Code repository | Visit project

voting app

Features of the app:

Authentication and access control:

  • Authentication with google.
  • Access control and authorization on 3 levels. Anonymous, logged in and owner of polls.

Users own their polls:

  • Users have CRUD functionality on their own polls only. Full CRUD functionality except for update. Update has been restricted to only allow new options, not modify existing ones.

voting app user polls


  • Polls are sorted by newest first.
  • There are 10 polls per page.
  • Every poll must have at least two options.
  • Anyone can vote on polls.
  • Voting has been restricted to once per poll for each user. A session/cookie is used for anonymous users to disable duplicate votes.
  • Chart displays poll results.
  • Chart always has a well-chosen range of different colors (but not tailored to color-blindness).

voting app single

Website design:

  • Simple and clean design.
  • Friendly flash / error messages with alerts which fade out.
  • Uncaught errors display a friendly message and redirect to the home page after a few seconds. (Try it by entering a non-existent path to get a 404 error.)


  • Access control and app security is quite vigorous (but hasn’t been thoroughly tested to the extent of a production app).
  • Uses popular security packages to prevent attacks.


Technologies used:

  • Node.js
  • ExpressJS
  • EJS template engine.
  • MongoDB and Mongoose ORM.
  • Git for version control.
  • Heroku for deployment.

Authentication and access control:

  • Authentication implemented with Passport.js and the Google authentication strategy.
  • Created middleware function isLoggedIn (ensures a user is logged in) and placed in routes required.
  • Users must be logged in to create polls.
  • Logged in users only have access and CRUD functionality to their own polls.

voting app edit poll

Database and models:

  • User model saves the GoogleID, a reference to the polls a user owns, and a reference to polls that the user has voted on.
  • Poll model contains the title of the poll, a created_at attribute storing when the poll was created for sorting purposes, and an array of objects containing an option and votes for that option. Each array also has a reference to the user that owns them.
  • Mongoose schemas have helper methods such as containsOption and voteForOption for better code structure.
  • The poll and user model have 2-way referencing. I did this as an exercise and for slightly faster read performance.

Higher order functions:

  • I used some higher order functions (closures) as helpers. For example to allow me to include a custom error message with the middleware function isLoggedIn, I created a higher order function which is called with a custom error message, and returns a function which behaves similar to
  • isLoggedIn, but has access to the custom message in its scope.
  • I also used a few higher order functions for refactoring purposes, allowing me to define functions elsewhere and also capture scope information.

User input:

  • I used express-validator to ensure all form input is valid according to poll specifications, and sanitised. Poll creation requires a title and at least 2 options, and they must all have a string length of at least 1.

voting app validation error

Code structure, including config files:

  • Standard code structure for a Node.js web app.
  • app.js
  • Views
  • Routes
  • public
  • models
  • Controllers
  • Helper functions
  • Config

Front end:

  • The focus was on the back end, so I used Bootstrap for an easy front end development, and a few custom styles.
  • Custom styles made with SCSS.

Flash and error messaging:

  • Used connect-flash and express-messages.
  • Express messages makes it easy for connect messages to be passed into views.
  • On the front end, I implemented flash messages with Bootstrap alerts.
  • I added a script which fades them out after a few seconds.
  • Custom error handler and view for displaying friendly error messages which are uncaught. Script redirects to home page after a few seconds.

Polls and voting:

  • Polls are sorted with newest first by having a created_at attribute and sorting against it.
  • The home page has pagination with 8 polls on each page.
  • The chart is created with Chart.js.
  • Palette.js is used to generate well-chosen colors for the chart depending on how many options there are.
  • Upon voting, a poll reference is recorded for registered users, or session information is updated for anonymous users, to disable duplicate voting.
  • Session information is stored in “MemoryStore” and is purged every 24 hours. I consider this sufficient for the predicted activity on this app, but it can always be changed to a database or memcache if needed.


  • All sensitive information hidden in environment variables.
  • Access control and app security is quite vigorous with many edge cases manually tested (but the app hasn’t been thoroughly tested to the extent of a production app).
  • Uses helmet for secure HTTP headers.
  • Uses secure cookies.
  • I did not use rate-limiters and CSRF protection, as this app contains no sensitive information and is not a serious target for attacks.


  • Method-override for delete and put requests without AJAX.
  • Using GZIP compression (“compression” module) for improved performance.

< Back to portfolio