Vertical Integration —Relating a Rails API Model to a React Component
One of the best pieces of advice I have received in building my own web applications has been to build vertically: to start with one feature of your application, and create all the necessary code for that single feature before moving on to other components. For example, in an app whose purpose is to keep track of one’s workout goals and progress, this could be the feature of displaying an individual workout). This is in contrast to “horizontal building”, where, for example, the entire back-end database for all features is completed before moving on to their front end display. Building vertically creates a sort of “scaffold” on which to add functionality later on, and gives you a sense early on of how data will flow through your app as a whole.
Here, I’ll summarize how I built a workout-display component for my workout tracking app, using a Rails API to handle data storage, and a React Component to display to the user.
The basic flow is as follows:
- A PostgreSQL database is created to save workouts and associated data to a database. Each workout is associated with a specific user through a unique user ID.
- The Rails API uses models, controllers, and routes in order to retrieve data from the database (or store new data) upon a user’s request, and sends that data to the website’s front end
- The front end stores the requested data temporarily in “state.” This allows it to load future requests quickly without a page refresh.
— — —
- The database
The database should hold data points representing the workout, but generally only those that cannot be calculated using other data points (e.g. BMI can be calculated using weight and height, so only the latter two would need to be stored in the database. Below is an API response representing a single workout in the database:
I knew I would later want to filter workouts by date and sport, so I included them in every workout. “Exert” is a 1–10 measure of how difficult the workout felt, which is a helpful way of judging progress over time. Much can be calculated later using very few data points (e.g. how many workouts per month on average? how many miles per week? am I most tired on Saturdays?), so I was happy keeping it minimalist.
2. Rails API
The API interacts with the database and packages/creates data when users click a button or submit a form. Rails turns each workout into a Rails object, and uses the controller to decide what to do with that data:
When a user wants to see their workouts, they send a HTTP GET request to the route that connects to the “index” action. This packages a user’s workouts into JSON format, which is sent across the internet in string format to the website’s front end.
When a user wants to log a new workout, the reverse happens — they send a JSON string over the internet to a route that connects to the “create” action. The JSON is turned into a Rails object, which can then be validated for correctness/completeness before saving. If something is wrong (e.g. you added a couple too many zeroes at the end of your run’s mileage), the API can send a JSON message back to the front-end with error messages. In sum:
Data flow when requesting existing data: Database → Rails object instance → JSON object (as a string)
Data flow when saving new data: User Form → JSON object (as a string) → Rails object instance → Database
In order to avoid submitting new data requests repeatedly while a user snaps around the website, the response received from an API has to be kept somewhere temporarily. This is called state, which can be manipulated as a user changes/adds/clicks on items throughout the website. When the front-end uses React, each component (e.g. a form, a speech bubble, an image with a caption) has a state separate from that of other components. Redux is a way to store and access information likely to be used by many components, like workouts, in a place called the Redux “store”.
Below is a function that fetches workout data from the API and adds it to the Redux store. If the user is logged in, we dispatch an action called “ADD_WORKOUTS”, which is a pure function that returns a new copy of the state with the updated workouts information from the database. While there is much complexity to Redux, it boils down to a way for your entire application to access/manipulate/analyze data without making excessive requests to the database and slowing the user experience.
4. HTML display using a React component
Once we have the data in our front end, we need to display it. Below is a React component named “Workout”, which is able to receive a workout object, in the same format as the JSON object initially passed to the state, as a “prop”, which can be used to populate out HTML elements. Helper functions like “capitalize” and “formatDate” (not shown) allow us to make raw data more presentable (i.e. ‘2021–04–01" → Thursday, April 1, 2021). After the workout information, I added a Delete button, which sends a request to the database to delete the workout based on its unique workout ID.
Together, these steps represent a high-level look at how a database and Rails API backend interacts with React components and Redux state. Designing a full feature vertically before moving on to others allowed me to see the structure of my web application in full before it got bigger and saddled with additional data tables and views. While this particular application will get more complex with time, adding new features is greatly simplified by creating this “template” off of which other components can be modeled.