DEV Community

newAlicorn
newAlicorn

Posted on • Edited on

Tasty Meals App - a SPA with a Ruby API backend and JS frontend

My Tasty Meals App is a SPA application built with a Ruby API backend and JavaScript frontend. Besides reading meal recipes from around the world, this app allows users to create, update or delete their recipes. Users can also search a meal by name and filter meals by category.

1. The Backend
The app contains a Rails API backend that follows RESTful convention. When I created the backend folder in the terminal, I used api flag to leave out the unnecessary features. Below is the command to create the API backend for the application:

rails new tasty-meals-app-backend --api

I have two models for this project, a category model and a meal model. The relationship between the two models is:
category: has_many :meals
meal: belongs_to :category

There are some new takeaways I've gained from this project when creating the backend:

Adding model relationship when using resource generator
Below are the command lines I used to generate all the resources.
rails g resource Category name
rails g resource Meal name thumb ingredients instruction category:belongs_to

We can omit the data type if it's a string. Also, we can specify the model relationship by adding belongs_to or references.

Since I already included --api flag, when I used the resource generator it would skip generating views and helpers for us. Also it will have the controllers inherit from ActionController::API instead of ActionController::Base.
Image description

Using Serializer to format data
There are mainly three ways to translate our Ruby objects into JSON format when our backend communicates with the frontend. We can directly include the format in the render method in the controller. We can also include the Serializer in the model class. In this project, I implemented the data translation by adding the active_model_serializers gem. Here is how:

In the gemfile, add gem 'active_model_serializers', '~> 0.10.2' and run bundle install.

After running rails g serializer category and rails g serializer meal in the terminal, set up the attributes in the files:
Image description
Image description

Upon setting things up as above, when we invoke the #index action in the Category controller, Rails will recognize automatically the serializer we include, instead of calling Category.all, we are actually calling the serialized data of categories using the attribute method we specified.

CORS
Since browsers do not allow unwanted HTTP requests from being sent to a server, they would restrict requests from a different origin. That's why we need enable CORS (Cross Origin Resource Sharing) to allow our server to specify from what origins it will permit.

2. The Frontend
On the frontend side, this app contains a single HTML page, styling with Bootstrap along with plain CSS. The app also uses Vanilla JavaScript codes to implement all the user interactions.

Using Classes
I created four classes for my project: the CategoryApi class and the MealApi class are responsible for handling all fetch requests for the categories and meals; the Meal class and the Category class enable to create an category or an meal object that encapsulates both data and behaviors.

Here is how I built out the Meal class to create an meal object and attach it to the DOM.

First I had the constructor prototypal method that was triggered every time when I invoked the new keyword to create a new meal object. The meal backend object was passed into the constructor as the argument of the constructor. Here I used the destructuring method (cleaner and more easily to read) to assign the values. Besides the properties of each meal, I also set up the HTML element that would contain this meal object, and attached event listeners particular to each meal object. Then, I created static all = [] to save all the meal objects. Every newly created meal object would be pushed into this array.
Image description

Static methods VS Instance methods
Most of the time, if I was dealing with a specific meal object, it’s gonna be a instance method. For example, below are two instance methods for rendering an single meal object and attaching it to the DOM.
Image description

When dealing with the collection of meal objects, I defined the static method as below.
Image description

3. Communication Between Backend and Frontend
All the interactions between the users and the server are handled asynchronously on my app. I included at least 4 AJAX calls that covers full CRUD functionality - create, read, update and delete a meal object. JSON is used as the communication format for my app.
Image description
The above code shows how I handles the meal collection sent back from the API using the fetch method. The flow is after I make a fetch request, I take the meal object from the backend and create a frontend meal object immediately. The newly created meal frontend object has a bunch of prototype methods and static methods we can call on and manipulate with, for example, the render method the attachToDom method.

4. Further Thoughts
This project has a lot room for improvement. For example, I still need to work on optimizing the search functionality. I also want to add a user login system to my project.

Top comments (0)