Tasty Meals App - a SPA with a Ruby API backend and JS frontend
Tags: beginners, javascript, ruby, rails
Published: 2021-11-01 15:51:28

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ugl5pg66svln5u76fbaq.JPG)


***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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tc53xmbezvjpj69sk1ui.JPG)
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jcnvvffvo8yuwz13n7a6.JPG)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xqp419i2u7z08lbxfs3g.JPG)

***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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sg2ldw4dpveraffgq184.JPG)

When dealing with the collection of meal objects, I defined the static method as below. 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udx3qefasnqoe9pbzzdd.JPG)

**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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3sq0t86u5u5rn14j7hjb.JPG)
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.