Building my First React App as a Backend Engineer

Bret Funk
9 min readDec 18, 2017

--

I am a recent graduate of the Turing School of Software and Design in Denver, Colorado. One of the core pillar’s of Turing’s philosophy is that you learn best when you are borderline overwhelmed. On my first day of Turing, the Executive Director and Founder of Turing, Jeff Casimir, told us he wants us to feel “like we are drowning” but, hopefully, not actually drown.

Turing Students and/or Navy Seals

This concept is on full display when, after seven-grueling-months, the backend engineering students had to do their capstone in a programming language new to them. Not only that, it better impress people because it is your ticket to getting a job. I can think of no other compliment to Turing’s stellar program than that we students weren’t even concerned about learning a new language so quickly, just annoyed!

I chose to build a language learning app, one that I have wanted to make since before I knew how to program. I am a fan of Anki and other Spaced Repetition Software (i.e. SRS, i.e. digital flashcards) but the challenge is that you are beholden to the sparse audio files available online from sites like rhinospike which often don’t have even basic sentences or the correct regional words/syntax. Especially since I am trying to learn Ecuadorian Spanish, which has it’s quirks (Their Spanish blends with Quechuan and they also use some Italian and English words).

How Ecuadorians talk to me

I wanted to learn as much as possible on this project so I chose to have a totally separate frontend and backend. I also chose to use React as the frontend and AWS’ S3 bucket system to store the audio files, both of which I had never used before.

Building the Basics

After seven months of using Ruby/Rails nonstop I built my backend pretty quickly. Since I had built a separate frontend and backend (something I had never done in Rails), I got stuck on CORS issues (something new to me) and having a Javascript Web Token (JWT) system to validate that users coming from the client were authentic. I decided to use the Knock gem because it was very lightweight and easy to use. The client would send a request to the backend server and if that user was in the database then the server would respond with a JWT that I could use with future API calls to access the current users information in the database.

The only issue I didn’t like about the knock gem was that I couldn’t access the current user’s ID from the JWT, something that is common with similar gems. So, I would have to make an automatic API call once the user was validated to know the ID so I could then make post/delete requests to that user’s information in the database.

Once the backend was built (except for the audio component) I moved on to the frontend. To hit the ground running, I completed a few hours of a basic React tutorial on both Codecademy and Udemy. Both were helpful but as I only had a little over a week to build my application I didn’t have time to go very deep. The big challenge for me was abandoning my much loved MVC file structure and not being able to write tests for the frontend. Here is my component file structure.

App.js
-Header.js
-NewUser.js
-Login.js
-MainWindow.js
-Body.js
-Form.js
-SideWindow.js (if user is logged in)
-Profile.js

The frontend was really just two different components. One, called the MainWindow, allowed the user to select a language, enter a phrase and then download it.

AnkiSound MainWindow (user isn’t logged in)

If the user was logged in then there would be an additional profile section, popped out on the right, that would list any phrases the user decided to save. In the profile, the user could also download any phrase or remove the phrase from the database. If the user logged out, the profile would be removed.

AnkiSound with user logged in (Profile open)

The frontend was fun to build but, being new to React, I had anxiety about having a massive App.js file that passed down data and methods to the various components. My App.js file has over 300 lines of code and it can be challenging to understand what does what with all of the props and data being passed up and down. After building this site, I was told that Redux solves some of these issues by allowing for a ‘store’ that holds the equivalent of global variables. If I had to rebuild this, or in a possible future update, I would incorporate that functionality and it would reduce my App.js file considerably.

A scroll of my App.js file, over 300 lines, I rarely break 50 in Rails due to SRP

Adding the Audio/Challenges

With so many new aspects of this application: separate frontend and backend, React, AWS, non-MVC architecture, dealing with audio files, etc. There were many challenges but the biggest was dealing with the audio. I tried many gems but every time I would download a file it would be random a gooblygock of letters and symbols. I assumed my download format was incorrect but I couldn’t figure it out. I asked for help from my teachers at Turing and they were also baffled. I could play the files just fine, just not download them in a readable format.

Eventually, I solved the problem using two crucial breakthroughs thanks to the advice of Nick Pierson, who built a similar website and was kind enough to help me after I cold-emailed him. The first breakthrough was plain ol’ HTML. Turns out, there is an html tag that allows you to automatically download a file if you know the url. Second, I had barely used the public folder before and didn’t realize I could use that to store the files and they would be accessible by the ‘public’. All I had to do was store the audio files to the public folder and then I could then use the HTML download tag to download the file. Eureka!

<a href="/public/en/i_will_want_an_apple_tomorrow.mp3" download>

Now that I knew I needed to store files inside the public folder, I knew I needed to set this up in the backend. I used the Rails gem ‘tts’ (talk-to-speech) to get the job done. Once I figured this part out, setting it up in Rails was easy. The big challenge was making sure that when the frontend sent a download request both the frontend and the backend were on the same page as to what the name of the file should be, and with 20+ languages, knowing the location of the file was also vital. I did this by naming the file in the frontend and passing in the name and the language to the backend. The backend would store the file in the public folder and then the frontend would access and download the file using the HTML download tag. Below is the Rails audio controller:

def index    
phrase = params[:phrase]
language = params[:language]
file_name = params[:file_name]
Audio.save_file(phrase, language, file_name)
end

Below is the code in the Rails Audio model. The class method is called in the audio controller and checks if the file is already in the public folder and if it isn’t, then save the file and name it a certain way:

class Audio < ApplicationRecord  
def self.save_file(phrase, language, file_name)
if File.file?
("./public/#{language}/#{file_name}.mp3")
else
"#{phrase}".to_file "#{language}", "./public/#{language}/{file_name}.mp3"
end
end
end

Then the client has a button next to each rendered phrase (cleverly named ‘Download’) that downloads the file using the HTML link download tag (after being converted to the correct format based on pulling the phrase data from the click event):

<a href={this.props.format(phrase)} download>Download</a>

Converting the phrase to the correct format:

format(phrase) {    
let language = languageHash[phrase.language]
let fileName = this.formatFileName(phrase.phrase)
let link = URL() + `${language}/${fileName}.mp3`
return link
}

AWS

AWS usage seems to be a programming right-of-passage. I had used Heroku for all of my previous applications but Heroku uses an ephemeral file system which means the filesystem will be destroyed when an app is restarted, reconfigured, etc. This means everything in the public folder is wiped clean regularly. So, if a user logs into AnkiSound 24 hours after saving phrases to the database then they wouldn’t be files left to download. My initial solution to this problem was to just make new files whenever a user logs into their profile but I wanted to use an AWS S3 bucket to learn more about AWS and the S3 system regardless.

AWS S3 buckets are essentially directories in the cloud but the AWS setup can be complex. I had used the Rails paperclip gem before in other Rails projects but many of the AWS gems assume some kind of static file (like a profile picture) that is connected to a user and the URL is saved as a string in the database. After looking at similar gems I didn’t see one that allowed has_many relationship (a user has many audio files, or even a many-to-many relationship with audio files) and the audio files were not being saved in a database, but in the public folder. For that reason, I assumed that I would have to do a lot more handrolling to get the process to work. I was wrong.

The most challenging aspect of hooking up AnkiSound to AWS was learning AWS. Since I had a separate frontend and backend AWS was not enthusiastic about letting an application that was not my backend (which did the uploading and had my AWS credentials) access the data. The way I got around this was to change the bucket policy and make the files publicly readable, and therefore downloadable.

Making these files public was not an issue for me as it was a small project but if I was making a more professional application I am sure there is an AWS gem for JS/React that would allow me to save my AWS credentials in the frontend to access them without the files needing to be public. However, I never found something like the Figaro gem that allows users to save confidential information as environmental variables in React. One of my teachers suggested we make a hidden API on the backend that the frontend can access someone hidden information from but that seemed like extra work for a still unsecure way of going about it. However, it didn’t matter.

I completed but never merged my S3 integration code to AnkiSound because I felt that it was not an optimal solution for my problem. Because I am adding an extra route of traffic (the saved file travels from the backend to AWS to be downloaded by the client instead of just saving in the backend to be downloaded by the client) I noticed that due to the distance and the size of the audio files there is a delay in the download.

If a user quickly creates a phrase and tries to download the file that the file will not be in the AWS bucket yet. This delay is only a spilt second but ultimately I am making an app for users so if the main functionality is compromised and the only benefit is that my application looks more impressive then I don’t believe that that is a good tradeoff to make.

Ever since I was a press secretary I asked myself, ‘could my mom understand this press release?’ Now that I am a software developer I ask myself, ‘could my mom understand this UX?’ Ultimately, my mom wins and I am keeping the application off of S3 for now.

An artists rendering of my mom

In Conclusion

Overall this is a fun project and seems to be the one potential employers want to talk about the most. I learned about audio files, downloading with HTML, React, a non-MVC file structure, separate frontends and backends, CORS issues and AWS S3 bucket storage. I am proud of this project but am not yet satisfied that it is as good as it can be. But there is always tomorrow.

Thanks for reading.

Click here to visit the live site.

Click here to view the frontend code.

Click here to view the backend code.

--

--

Bret Funk

Software Developer. Former Navy, commercial pilot, congressional spox and small biz owner. 🇺🇸🇪🇨 :wq