outLANdish designs

Posted by Lan Nguyen on January 9, 2020

This application was built utilizing a Ruby on Rails backend with session cookies for API authentication and a vanilla JavaScript frontend.

E-Commerce

I was excited to be able to incorporate a personal side business into this project and see it come to life . This project was an e-commerce application for my small custom T-shirt business. This application was very challenging to build. Just implementing the authentication process took multiple days to figure out. The backend consists of multiple join tables to form the cart, category, and wish list functionality.

Rails API Backend

The API backend uses a Model-View-Controller structure with CRUD actions and RESTful routes. The views are omitted because it is communicating with a JavaScript frontend. The core model associations were:

class Account < ApplicationRecord
    
    has_one :cart
    has_many :orders
    has_many :reviews
    has_one :wishlist
    
end


class Cart < ApplicationRecord

    has_many :cart_items
    has_many :items, through: :cart_items
    belongs_to :account, optional: true
		
end


class CartItem < ApplicationRecord

    belongs_to :item
    belongs_to :cart

end


class Item < ApplicationRecord

    has_many :cart_items
    has_many :carts, through: :cart_items
    has_many :order_items
    has_many :orders, through: :order_items
    has_many :reviews
    has_many :wishlist_items
    has_many :wishlists, through: :wishlist_items
    has_many :item_categories
    has_many :categories, through: :item_categories
		
end

JavaScript Frontend

The frontend folllows a decorator design pattern where functionality of a base class is decorated and used to separate functionality for the different adapters and page managers used in fetch requests.

class BaseAdapter{

    constructor(baseURL = 'http://localhost:3000/api/v1'){
        this.baseURL = baseURL
        this.authSetup()
        this.csrf_token = null
    }

    async authSetup(){
        const res = await fetch(`${this.baseURL}/auth`, {
            credentials: 'include'
        })
        const body = await res.json()
        this.csrf_token = body.csrf_auth_token
    }

    get headers(){
        let baseHeaders = {
            'Accept': 'application/json',
            'Content-Type': 'application/json',
            'X-CSRF-TOKEN': this.csrf_token
        }
        return baseHeaders
    }
}

class CartAdapter{

    constructor(baseAdapter){
        this.baseAdapter = baseAdapter
        this.baseURL = this.baseAdapter.baseURL
    }


    get headers(){
        return this.baseAdapter.headers
    }
        
				
    async showCartItems(){
        try{
            const cartItemResponse = await fetch(`${this.baseURL}/mycart`, {
                method: "GET",
                headers: this.headers,
                credentials: 'include'
            })
            const cartItemJson = await cartItemResponse.json()
            return cartItemJson
        }catch(error){
            console.log(error.message)
        }
    }


    async removeFromCart(currentId){
        try{
            const removeResponse = await fetch(`${this.baseURL}/cart_items/${currentId}`, {
                method: "DELETE",
                headers: this.headers,
                body: JSON.stringify({item_id: currentId}),
                credentials: 'include'
            })
        }catch(error){
            console.log(error.message)
        }
    }
}

Project Outcome

This project had major hurdles and took much longer than expected to execute but overall, functionality is what I had envisioned. There is still quite some work to do in order to implement the checkout process, review, and wishlist functionality.