Building a Full Stack Project Using a Custom-Built Rails API

Sumaiya Tabassum
The Startup
Published in
9 min readOct 20, 2020

The following blog post is a tutorial on building a Full Stack project using JavaScript and a Rails API. The link to the GitHub repository is down below:

Note: The following tutorial will be updated as needed.

Intro: JavaScript & Rails

JavaScript is a powerful object oriented programming language that can be used to build web applications. JavaScript is a multipurpose and a convenient programming language that allows for both front-end and back-end development.

Project Domain:

The domain for the Full Stack application project in the following tutorial is a course registration project. This project mimics a registration application in which an administrator (Admin) would register a student into a particular course.

Project File Structure:

When building an application, it is important to understand the flow of the application and the file structure. Keeping folders and files organized in a structured manner will allow the developer to understand the application structure and refer to a particular section of code faster. This allows for easier debugging.

For the Full Stack project, we will divide the user interface from the application logic, in other words, the front-end from the back-end of the application. Below is the file structure of the application:

student-course-rails-js
├── student-course-backend
│ ├── app
│ │ ├── controllers
│ │ │ ├── courses_controller.rb
│ │ │ └── students_controller.rb
│ │ ├── models
│ │ │ ├── course.rb
│ │ │ └── student.rb
│ │ └── views
│ └── db
│ ├── migrate
│ │ ├── 20200928184552_create_courses.rb
│ │ └── 20200928184626_create_students.rb
│ ├── schema.rb
│ └── seeds.rb

├── student-course-frontend
│ ├── src
│ │ ├── course.js
│ │ ├── index.js
│ │ └── student.js
│ ├── style
│ │ └── style.css
│ └── index.html
└── README.md

What are APIs?

The acronym ‘API’ stands for ‘Application Programming Interface’. An API allows for one system to communicate easily with an external system. APIs can generally be sources of data that can be used for building applications, such as movies for a particular theatre, weather information, number of people affected by a particular disease (example: COVID-19) based on geographical location and much more.

Rails is a multi-functional framework and one of its special features is to allow the creation of an API from scratch. If there are particular information that you would like to share, you can build an API that just shares that information and deploy it to the web. The data in an API can be organized in a particular way.

Rails Backend API

Before working with Rails, make sure you have Rails installed into your local machine. To create a Rails API from scratch, run the following command on your terminal:

rails new student-course-backend --api

The ‘api’ flag creates a rails api with all of the files necessary for creating an api. The student-course-backend represents the name of the api. When using the api flag, in contrast to not using it, certain files that are generally included when creating a rails application are excluded when creating a Rails api, as these files are not necessary for an api.

The backend api file structure should look something like this :

back-end
|__app
|__bin
|__config
|__db
|__lib
|__log
|__public
|__storage
|__test
|__temp
|__vendor
|__config.ru
|__Gemfile
|__Gemfile.lock
|__Rakefile

The folders that we will be working on for the backend api are the following:

  • app
  • config
  • db

We will also work with the Gemfile. In the Gemfile, uncomment the gem ‘rack-cors’.

gem 'rack-cors'

The visibility of the above gem will allow the host of the application to interact with the api.

We will now focus on the database portion of the application. Navigate to the “db” folder and create a “migrate” folder to store migration files. The migration ruby files are the following, located beneath the migrate folder. You can either use shortcut command or manually create them. Either is fine.

db
|__migrate
|__
20200928184552_create_courses.rb
|__
20200928184626_create_students.rb

Within each migration file, add the necessary information needed to create the schema file.

The file, 20200928184552_create_courses.rb has the following code:

class CreateCourses < ActiveRecord::Migration[6.0]
def change
create_table :courses do |t|
t.string :name
t.string :description
t.timestamps
end
end
end

The file 20200928184626_create_students.rb the following code:

class CreateStudents < ActiveRecord::Migration[6.0]
def change
create_table :students do |t|
t.string :full_name
t.string :email
t.string :time_preference
t.references :course, null: false, foreign_key: true
t.timestamps
end
end
end

Once the migration files have been created, save the files and run rails db:migrate on your terminal to create your schema file. Once the schema file is created, under the “db” folder, create a seed.rb file to input dummy data to work with your application:

Once the code has been added, return to your terminal screen and run the following command:

rails db:seed

To confirm if the seed data has been inserted into the database, run the rails console command on your terminal to open up the console.

To view all of the courses or students for data seeding confirmation, you can run either one or both of the following commands in the console:

Courses.all# or run the following command in the consoleStudents.all

Models

After the database has been set up, move on to the models folder located within the app folder. Here we’ll create model files, course.rb and student.rb and established the relationship between the two.

class Course < ApplicationRecord
has_many :students
end
class Student < ApplicationRecord
belongs_to :course
validates :full_name, presence: true
validates :email, presence: true
validates :time_preference, presence: true
end

The above established relationship will have the necessary methods to interact with the database by inheriting from ApplicationRecord, which inherits from Active Record (an Object Relational Mapper).

To ensure user inputs entries in a form for certain fields such as full name, email, and time preference before submission, we can use validations. Validations will ensure that the user must input some text in that field before submitting the form.

Serializers & Controllers

Serializers

When coding, it is best practice to keep your code as DRY (“Don’t Repeat Yourself”) as possible. This means, redundant code that appears throughout your program, must somehow be “summarized” and/or reduced.

In our controllers, there may be occasions in which both the index and show method, or any other method, use the same lines of code over and over. We can store these recurring codes in serializers and later call them in our controllers.

  1. In the app folder, create folder titled “serializers”.
  2. Create two files in the serializers folder and write the following code:
# File Name : course_serializer.rbclass CourseSerializer
include FastJsonapi::ObjectSerializer
attributes :name, :students
end
# File Name : student_serializer.rbclass StudentSerializer
include FastJsonapi::ObjectSerializer
attributes :full_name, :email, :time_preference, :course
end

** Note: Be sure to include the “fast_jsonapi” in your gemfile in order to use the serializers.**

Controllers

The controllers allow for the communication between model classes, databases, and the functionalities to be carried out on the front-end. We will set up two controllers for the purpose of this application along with a few controller methods. The methods will be implemented in our application for use on the frontend portion of the project.

# create a file under the controllers folder:
# courses_controller.rb
# Add the following code in the controller
class CoursesController < ApplicationControllerdef index
courses = Course.all
render json: CourseSerializer.new(courses)
end
def show
course = Course.find_by(id: params[:id])
render json: CourseSerializer.new(course)
end
def destroy
course = Course.find_by(id: params[:id])
course.destroy
end
end# create another file under the controllers folder:
# students_controller.rb
# Add the following code in the controller
class StudentsController < ApplicationControllerdef index
students = Student.all
render json: StudentSerializer.new(students)
end
def show
student = Student.find_by(id: params[:id])
render json: StudentSerializer.new(student)
end
def destroy
student = Student.find_by(id: params[:id])
student.destroy
end
end

Routes

The next folder we need to work with is the config folder. Within the config folder we must ensure that our routes are defined in the routes.rb file.

# The following will allow all of the controller pathsresources :students
resources :courses

Once the paths are defined in the routes, let us locate to the cors.rb file in the initializers folder under the config folder.

Uncomment the code starting from “Rails.application …” and paste the following :

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

The above will work with the uncommented gem “gem rack-cors” allowing the host of the application to interact with the api.

(Note: Majority of what we have done up to this point with the back-end rails API structure of the application can be automatically done by rails generators. However, for better understanding of files and structure, some manual work was shown in the tutorial).

Frontend

Once the backend is set up and working, we can use the data and logic to communicate with the frontend and display content to the browser. While building on the frontend portion of the application, there are a few steps from diagramming, to writing HTML and CSS content, to finally bringing interactivity to the site through JavaScript.

Draft Diagram

Before going straight into coding, it is good practice to outline, or at the very least diagram a draft of the application. Diagramming will help the developer get a visual idea of what to code in HTML faster. Despite knowing what functionalities a developer may want to add to an application based on the developed backend, a diagram can always be useful.

Below is the draft diagram of what the user would interact with on the frontend with the expected functionalities.

“…it is good practice to outline, or at the very least draft a diagram of the application.”

HTML

Based on the diagram, the user inputs information in one form, and depending on the input information, a “card” is displayed for a particular course a user registers for. The input for itself is static in that a user is directed to some input form as soon as they enter the website. The information that is entered by the user which is displayed is dynamically generated through JavaScript.

So, for the HTML, let us build the user input form:

<!DOCTYPE html><html lang="en"><head><!-- make sure to have all of the meta tags -->         
<title>Bootcamp Registration</title>
<link
rel="stylesheet" href="style/style.css">
<script type="text/javascript" src="src/course.js"></script> <script type="text/javascript" src="src/student.js"></script> <script type="text/javascript" src="src/index.js"></script>
</head><body><div id="container"><form id="card-form" class="card-registration-form"><h3>Technology Bootcamp Registration Portal</h3><p><em>
<b><u>
Admin View : Permission to Register Students</u></b>
</em></p>
<label>Full Name: </label>
<input id='full-name' type="text" name="fullname" value="" placeholder="Full Name" class="input-text">
<br><br>
<label>E-mail: </label>
<input id='email' type="text" name="email" value="" placeholder="Email" class="input-text">
<br><br>
<label>Time Preference: </label>
<input id='time-preference' type="text" name="timepreference" value="" placeholder="Time Preference" class="input-text"> <br><br>
<label>Bootcamp Selection</label>
<select id="bootcamps" name="bootcamps">

<option value="software_engineering">
Software Engineering Bootcamp
</option>
<option value="cybersecurity">Cybersecurity Bootcamp</option> <option value="data_science">Data Science Bootcamp</option> </select>
<br><br>
<input id="create-button" type="submit" name="submit" value="Register" class="submit">
</form>
</div><br>
<div
id="cards">
<h3
style="text-align: center;">Currently Enrolled Students</h3> </div>
</body></html>

CSS

Once the HTML is coded, the CSS file can be set up with some initial code to style. Most of the styling for this application was done through inline CSS (styling within HTML elements).

JavaScript

To allow behavior and communication within the application, we want to use pure JavaScript for this particular application, rather then Ruby. To do this, allowing the database objects to talk and bring a dynamic site, we want to write object-oriented code in JavaScript. Let us first organize our files, and separate the logic into three JavaScript files under the src folder:

course.js
student.js
index.js

Having three separate script files will allow to focus on specific concepts. The course.js file can focus specifically on course objects, student.js can focus on student objects, and the index.js file can focus on what happens on the browser based on the users interactivity with the application in relation to the student and course objects. Below, we can summarize what is happening in the respective files for the JavaScript:

// course.js// class Course {}
// Implement course as a class data structure
// student.js// class Student()
// Implement student as a class data structure
// index.jsconst firstEndPointOfAPI = "some URL";
const secondEndPointOfAPI = "some URL";
document.addEventListener("DOMContentLoaded", ()=>{// code implementation of form submission
// render data to form
})// Add additional functions to create and delete instances

AJAX (Asynchronous JavaScript XML) Calls

AJAX prevents the constant load and reload so that users do not navigate away from a website or application due to long waiting time. AJAX techniques allows the HTML and CSS to be rendered to the browser and allow additional behavior to run on the application. Using fetch() calls helps apply AJAX techniques in which loading additional data is presented after information is presented to the user. In our JavaScript we used fetch() multiple times to retrieve data. In general, the format is as follows:

// fetch() callfetch("string URL to retrieve data")
.then(resp => resp.json())
.then(json => console.log(json));

Conclusion

The Rails framework has many features that makes application development faster. One key feature of Rails is that it allows developers to build custom restful APIs for their applications. The API can interact with frontend development tools such as HTML, CSS, and JavaScript through fetch() calls — AJAX implementation, and make user-friendly applications.

--

--

Sumaiya Tabassum
The Startup

Computer Science — Psychology — English Literature