A Fullstack Challenge: E-commerce with Vanilla JS
Published: 2021-04-11 23:04:13

To challenge myself and how much i've grown while learning javascript, I decided to mimic a functioning e-commerce website but with a twist.

*I have to make it a Single Page App AND only use vanilla Javascript.*

<hr>*Links to the github repositories([frontend](https://github.com/geraldarzy/thrifty-frontend) | [backend](https://github.com/geraldarzy/thrifty-backend))*
<br>

To start off the project, I decided to use Ruby on Rails to get my backend up and running as fast and efficient as possible. For the frontend, as mentioned, I used vanilla Javascript with no frameworks. And of course some Bootstrap to make my HTML appealing. 


Lets break this down into three sections, the [backend](#backend), the [frontend](#frontend), and some [challenges](#challenges) I faced.


### Backend <a name="backend"></a>
##### Rails as an API
With Ruby on Rails handling my backend server, I designed an API that would handle all the information I need. An API would let me make calls to my backend server from the frontend so it seems like a perfect way to handle this. 
##### PostgresSQL
To handle and store information, I used a SQL database and went with 'PostgreSQL'. I went with SQL because Rails has an excellent way of handling SQL relationships through a Ruby Gem `ActiveRecord`. This makes the models a lot easier to handle because then each User would now own a cart in which each cart has many items, etc. 
##### Authentication
For authentication of a User account, I chose to challenge myself again and not use a framework that generates all the proper models, routes, controllers, and actions (i.e. Devise). I used a low level Ruby Gem 'BCrypt' in which all it does is handle a string (the password) to make sure it is kept encrypted and safe, but still accessible to be able to match against the provided password by the a user trying to login (password is encrypted and matched against the encrypted password because we do not hold onto any un-encrypted data).

##### Models
| Models        | Data it stores         | Relationships |   
|:-------------:|:-------------:|:-------------:| 
| User     | name, email, home address | <ul><li>belongs to a cart</li>
</ul> |
| Store      |name, address, website       |<ul><li>has many items</li>
</ul> |
| Items | name, price, size, color, picture, store_id, sex_id     | <ul><li>has many carts through cart_items</li><li>belongs to a store </li><li>belongs to a sex</li>
</ul> |
| Sex | name      | <ul><li>has many items</li>
</ul> |
| Cart | user_id      | <ul><li>belongs to a user</li><li> has many items through cart items</li>
</ul> |
| Cart_items | cart_id, item_id      | <ul><li>belongs to a cart</li><li>belongs to an item</li>
</ul> |

Without some context of the type of e-commerce site this is, these relationships might confuse you. The reason that each item belongs to a store rather than each item belongs to many store, is that each item is unique and no two items are the same because it is a Thriftshop website where you can buy vintage clothing. Therefore an item can only belong to one store because no two stores will have the same item.

The last model is a model that the user interacts with but never actually sees. It is a join table to join an item with a cart when a user 'adds an item' to their cart. And since each cart already belongs to a user, a user has many items through their cart.

The model for sex makes the sorting feature for the store easier whereas since each item is linked to an instance of sex, we can just access all the items that belong to each sex when you want to sort by a specific sex.

##### Controllers
For the controllers, each model has their own controller handle the workload of a user trying to access each model, where it be they are trying to 'get' information, or 'post' information to the database. 
These controllers handle 'get' requests that vary from getting a list of items, to getting information about an item. 
The 'post' requests vary from requests to 'post' new information with a user trying to sign up to a request for a user trying to link an item to their cart.

In addition to these controllers, we have another controller that deals with the Session for which there is no model for. This 'Session Controller' handles all the action of logging a user in or out. This is separated into its own controller because it deals solely with the *Session* cookie in which 'logging in' is essentially just telling the session cookie your user_id.

### Frontend <a name="frontend"></a>
Now onto the frontend of this project!!
Since I worked only with pure Vanilla Javascript, I decided to spread my functions out into class static methods in order to try and organize things up a bit. Also since were doing a Single Page App, there is a lot of clearing the DOM of elements to then append different elements into it. 

#####Classes
| Class Names   |Responsibility|
| :-------------: |:-------------:|
| User     | <ul> <li>retrieve information about the user object in the database</li> <li>handle the cookie on the frontend</li></ul> |
| Store| <ul> <li>retrieve a list of stores and information about each store from the backend</li></ul>      | 
| Item| <ul> <li>retrieve a list of items based on the parameter passed into its method</li> <li>make a container element and append it to the DOM for each item</li></ul>   | 
| Cart| <ul> <li>retrieve a list of items from users cart from the backend</li> <li>make a container element and append it to the specific cart modal for each item </li></ul>    | 
| Session| <ul> <li>handles the cookie when a user tries to log in or out</li></ul>    | 
| Modals|<ul> <li>makes and appends modals to the DOM for other methods to be able to append to this modal</li><li>adding listeners to certain buttons to make the modals more responsive</li></ul>   | 
| Showpage| <ul> <li>clears the DOM and handles the logic on which elements to append to the DOM</li> <li>also in charge of activating the hidden modals when needed</li></ul>   | 

##### SPA - A Single Page App
Part of the challenge of this project was to make a Single Page App. Meaning no rendering or reloading of a new HTML page. This meant that I was constantly clearing the DOM of elements and creating and appending elements with javascript. 

This was great practice as it was really tricky trying to dynamically clear the page of certain elements and dynamically *create* and *append* new elements. All in all, I was able to achieve this and separate those actions into the Showpage class. 
`Showpage.clearPage()` was in charge of clearing the second element of the body until the count of the elements inside the body was down to 1 single element. This element being the nav bar and therefore we never wanted to get rid of that. 
Then came several other methods that created and appended elements to the newly cleared DOM. 

### Challenges <a name="challenges"></a>

#####A frontend challenge - Working with encrypted data from the backend to the frontend
A challenge I had was trying to access important information the I retrieved from the backend. The way to go about this is to pass these important information through the header of a request.  This is the preferred method because then no one else will be able to access this because Javascript cannot access this 'session cookie' from the backend. Although there were alot of development issues and without a powerful framework like 'React' or 'Vue', working with a 'session cookie' from the backend was almost impossible. Once I was able to send the proper request via adding a `credentials` header to the request, I then ran into 'CORS' issues (Cross-Origin Resource Sharing). I chose to forego this issue as I had already taken up several challenges. I chose to workaround this by using regular cookies instead. Now this is bad practice since anyone who knows slightly what their doing, can edit these cookies to their liking and access things that should not be accessed to them. With this in mind, All the info that is accessible via cookie, are all information that cannot be used to exploit another user. Basically, editing a cookie to change your user_id will just give you access to another persons cart but nothing else. Even with this vulnerability I continued on with the project because the point of this project and all these extra challenges I put on myself was more so to improve my skills and get some practical experience building something. I even like to think that now that I've used bad practice, it's easier for me to value the importance of good practice and also easier for me to realize something that is done with bad practice. 

##### Dealing with Nested JSONS for the API

Part of handling the backend is handling of how the information is organized when it is given to a request. Say the frontend made a request to get a list of items. Now we could give back a list of items and call it a day. But that would make the user experience terrible because sure now they have a list of item names but what about all the other info on the item, the price? the size? All this information is kept on the backend so why not make use of it right? When it came down to it, I decided to take on another challenge and organize the JSON myself rather than offloading the work to a serializer. Since we were working with a JSON object, I had to nest the information. Since we were working with SQL, it was as easy as nesting the store information in the items 'store_id' field since each item already had an idea of a 'store', we just had to give the item a bit more information.

### Things to Improve

##### Checkout System

With a deadline of a week, I was able to knockout the items to cart to user relationships and functionality. Not to mention dealing with the encryption of the user data. The one thing that I did not get to implement was checking a cart out. This is a feature I want to add in the future as it will really tie the whole project together. My idea is to have a new model in the backend to keep track of all the orders. Orders will be created when a user successfully purchases a cart and each order will have knowledge of all the items on the order, as well as the user who made the order. With each order creation, in the same action, the items which were purchased should also be deleted from the items list and moved into the orders list. This would ensure that no two people can purchase the same product (its a thriftshop so each item is unique). Lastly to deal with the payments for each order, I would like to make use of an API 'Stripe' who specializes in giving transaction services to those who use it. 

Overall I think this was a great project and exposed me to alot of key javascript features (asynchronous functions, element querying, listeners, etc.). It also solidified the knowledge I have in the backend as I was able to tie the two things together to make a single functioning app. 

I also wrote about [Asynchronous vs. Synchronous Functions](https://dev.to/geraldarzy/understanding-asynchronous-vs-synchronous-programming-in-javascript-4ann) because of the horrible time I went through when I figured out what Asynchronous Functions were by myself (basically a couple of  hours of banging my head on the table because I had no idea why my list wasnt populated but then suddenly populated at a certain point). With hopes of saving others from that same headache, I wrote about asynchronous functions in relation of real life synonyms in order to explain it more simple.