A Single Page Application for storing Recipes with a JavaScript frontend and a Rails API backend
Published: 2021-11-12 15:45:31

I set out to build a simple [single page application (SPA)](https://en.wikipedia.org/wiki/Single-page_application) which utilized a JavaScript frontend and a Rails API backend in order to store recipes.

I started by setting up the Rails API backend. I navigated to the directory where I wanted to create the project, and then ran the following command in the terminal:

```
rails new meal-maker --api
```

The `--api` flag set up a Rails application named meal-maker, but without many of the standard features and middleware that Rails would have come with if I had run the command without adding the `--api` flag.

With this API-only Rails build set up I then created the models, controllers, and migrations that I would need.

I decided to have two models, a cuisine model and a recipe model with has_many and belongs_to relationship between them where a cuisine has many recipes and a recipe belongs to a cuisine.

To quickly create the migrations, models and controllers for the cuisines and recipes I ran the following two commands in the terminal:

```
rails g resource cuisine title

rails g resource recipe title image instructions ingredients cuisine:references
```

The first command created a cuisine migration file to create a cuisines table with a title column with a string data type, along with a cuisine model and a cuisine controller.

The second command created a recipe migration file to create a recipes table with columns for title, image, instructions, and ingredients, all of the string data type, along with a cuisine_id column to indicate the particular cuisine that a recipe belongs to.

In addition a recipe model was created and a recipe controller was created after running the command. My models were set up as follows:

```Ruby
class Cuisine < ApplicationRecord
    has_many :recipes
end
```

```Ruby
class Recipe < ApplicationRecord
  belongs_to :cuisine
end
```

The recipe model already had `belongs_to :cuisine` filled in after running the second command above, but for the cuisine model I had to enter `has_many :recipes` there myself.

I ran the migrations that had been created using the command `rails db:migrate` and also filled out the two controllers with the necessary actions (controller methods) that I would need for the functionality that I wanted my application to have.

I also filled out the seed file with information for four different recipes from four different types of cuisine that I would use to see how the application was working as I was building it out. Then I ran the command `rails db:seed` to seed the database with the instances of the cuisine and recipe classes.

In the cors.rb file in the config/initializers folder I made sure that the following code was uncommented out:

```Ruby
Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    origins '*'

    resource '*',
      headers: :any,
      methods: [:get, :post, :put, :patch, :delete, :options, :head]
  end
end
```

And I made sure that the line which originally said `origins 'example'` was now changed to `origins '*'`. 

In my Gemfile I also uncommented out the line which had `gem 'rack-cors'` commented out. I then ran the command `bundle install` in the terminal.

With those changes made to those two files I made sure that problems relating to [Cross-Origin Resource Sharing (CORS)](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) would not cause problems for the ability of the JavaScript frontend to communicate with the Rails API backend.

After this I moved all the folders and files in the application except the README.md file into a new folder that I created called "backend". 

Then I created a new folder called "frontend" and in that new folder I created an index.html file and a style.css file. In that frontend folder I created another folder called src, and in the src folder I created an index.js file.

In the index.html file I built out all the [HTML](https://developer.mozilla.org/en-US/docs/Web/HTML) elements that my application would use, consisting mainly of a form to add new recipes as well as a container to hold already available and newly added recipes.

In the style.css file I defined the visual styling/appearance of the page using [CSS](https://developer.mozilla.org/en-US/docs/Web/CSS), then I made sure that the style.css file was linked to the html file by making sure to include the following line in the `<head>` section of the index.html file:

```HTML
<link rel="stylesheet" type="text/css" href="style.css" />
```

I also made sure that the index.js file would be linked to the index.html file by including the following line in the `<head>` section of the index.html file:

```HTML
<script type="text/javascript" src="src/index.js" defer></script>
```

In the index.js file inside of the frontend/src folder I used Object Oriented JavaScript to write out a Recipe class with the methods and behavior that I wanted to provide my application with the necessary functionality. 

I wrote [static](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/static) and non-static methods that used DOM manipulation or [Document-Object-Model manipulation](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Client-side_web_APIs/Manipulating_documents) on the elements of the index.html file, as well as methods that made three [AJAX calls](https://developer.mozilla.org/en-US/docs/Web/Guide/AJAX/Getting_Started) using [fetch](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch) in order to [GET](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/GET) and [POST](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST) data from and to the Rails API backend that I had set up earlier. I was able to communicate with the Rails API backend using `fetch` in these JavaScript methods after making sure to first navigate to the backend directory of the application and then use the `rails s` command to start the server. 

One of these methods I defined for the Recipe class was able to use `fetch` to get all of the Recipe objects, including information about what Cuisine each Recipe object belonged to, from the backend and pass on that information to another method to display these recipes on the page. Another method was able to use `fetch` to make new Recipe objects from the information entered by the user on the form at the top of the page, and then pass on the entered information to another method which would display this newly created recipe in a table row on the page.

Finally another method was able to remove existing recipes from the page of the application and use `fetch` to delete existing recipes from the Rails API backend once the user clicked on the delete button below a recipe. All of this functionality was possible in the application without the page needing to be refreshed/reloaded, showing the power of a JavaScript frontend to deliver a fast and seamless experience for the user.

I learned a great deal about JavaScript, HTML, and CSS from building out this project and I felt like I was closer to becoming a full stack software developer after it was completed.

[A link to the repository for my project](https://github.com/GuosaE/meal-maker)