/easycal-backend

Java backend server for http://easycal.io

Primary LanguageJava

EasyCal Backend

This is the backend server to the EasyCal React.js application: http://easycal.io

Technology/Libraries

  • Play! 1.4.4: Java MVC web framework (I only really used the M and the C 😉). Exposes REST API endpoints with JSON responses to communicate application data between the front- and back-end.
  • MySQL: model definition and persistence in a relational database
  • Hibernate ORM: provided object-relational mapping for easy data retreival and manipulation. Object models mapped using JPA annotations

Dependencies

  • JSON-P: a JSON utility library, makes building JSON responses a breeze
  • org.json: another JSON util library, super helpful in parsing request bodies composed of JSON
  • jBCrypt: Java implementation of the BCrypt password hashing algorithm; used to securely save and verify user passwords
  • Java JWT: API for creation and verification of JSON Web Tokens (see Authentication)

Data Model

All models reside in app/models/.

Application Data Model (shoutout to Quick Database Diagrams for this great schema visualization)

  • FoodItem: the core model of the entire application -- represents a single food
  • Consumption: records the event of a User (FK user_id) adding a FoodItem (FK food_item_id) to his/her log for a given meal on a given day
  • ServingSize: represents a type of serving (i.e. cup, oz, slice). The USDA database standardizes nutrition info in terms of 100 gram servings, so the ratio field indicates the serving size's ratio to this standard. For example, one cup of shredded cheddar cheese weighs 113 grams, so its ratio is represented as 1.13
  • ServingLabel: some serving size names will appear very frequently (like cup, oz, slice, and piece), so this serves as a lookup table for just these labels. At a larger scale, this would prevent excessive duplication of serving label strings in the ServingSize table
  • CreatedFood: created_food is a join table that relates created FoodItems (FK food_item_id) to the Users (FK user_id) who create them
  • Exercise: describes the amount of calories a User (FK user_id) burned on a given day
  • Goals: each goal is an amount of a nutrient or a calorie value that a user wishes to reach every day. The goal_category field is an ordinal reference to the app/models/Goal.GoalCategory enum whose values consist of: CALORIES, CARBS, FAT, PROTEIN, FIBER, SUGAR, SODIUM

All methods that interact with data are located in app/controllers/EasyCal.java and app/util/DatabaseUtil.java.

Authentication

Since EasyCal is an application that involves user accounts and individual data, there must be some form of a secure authentication system. Instead of going with the traditional cookies route, I decided to use JSON Web Tokens (JWTs). The payload of the tokens consists of the following:

{userId: xxxx, emailAddress: foo@bar.com, exp: 15284xxxxx}

This provides all the information needed to identify the user. Upon registration or login, the buildJWT(user) method in app/controllers/Auth.java creates a new token using the Java JWT library (see Dependencies), which is then persisted in the client's LocalStorage.

To keep routes such as /, /add, /stats, and /me protected, the frontend sends a request to Auth.checkAuth() to ensure the logged-in user's JWT is valid (the signature matches and the expiration date is not passed). Furthermore, the Play! framework has a helpful @Before annotation to ensure that the user's token is checked prior to every backend request in EasyCal.checkAuth().

If the user's token is absent or invalid, the server responds with a 403 Forbidden HTTP code, and the frontend clears the user's storage and redirects him/her to /login.

Deployment

This backend server is deployed on an Ubuntu virtual machine through a Digital Ocean droplet. It only has 1 GB of RAM so if this becomes the next MyFitnessPal, I'm toast. 😅 To get the app online, I build a zipped WAR file, then upload it to the server using Apache Tomcat. I bought a cheap domain (not easycal.io) to enable SSL, so the frontend can communicate with it securely through basic HTTP requests.