DEV Community

Cover image for A Single Page Application for storing Recipes with a JavaScript frontend and a Rails API backend
Guosa
Guosa

Posted on • Edited on

A Single Page Application for storing Recipes with a JavaScript frontend and a Rails API backend

I set out to build a simple single page application (SPA) 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
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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:

class Cuisine < ApplicationRecord
    has_many :recipes
end
Enter fullscreen mode Exit fullscreen mode
class Recipe < ApplicationRecord
  belongs_to :cuisine
end
Enter fullscreen mode Exit fullscreen mode

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:

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
Enter fullscreen mode Exit fullscreen mode

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) 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 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, 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:

<link rel="stylesheet" type="text/css" href="style.css" />
Enter fullscreen mode Exit fullscreen mode

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:

<script type="text/javascript" src="src/index.js" defer></script>
Enter fullscreen mode Exit fullscreen mode

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 and non-static methods that used DOM manipulation or Document-Object-Model manipulation on the elements of the index.html file, as well as methods that made three AJAX calls using fetch in order to GET and 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

Top comments (0)