Fashion, NYC, API Wrappers

April 30, 2023

Moving to New York

During my last semester of college, I was given the opportunity to work at a startup called Ouway. The issue was I had to move to New York.

I was hesitant at first, having never lived in a big city before and being uncertain about the cost of living. However, I decided to take the chance because it was a great opportunity to work in the fashion industry and gain experience as a software engineer. After doing some research, I found a tiny studio apartment in Manhattan.

For the past year, I was working diligently, trying to bring the project to market, sadly though because of the recession I was sadly let go. However, I experience I gained during my time at Ouway was invaluable.

What was Ouway?

Ouway planned on innovating the shopping experience through a same-day delivery/return service. The focus was meeting the needs of today's consumers ensuring that they can receive their purchases on the same day they place their order, providing an exceptional level of convenience and satisfaction.

One of the main focal points of Ouway was populating the app with products that we could ensure could be delivered/returned to the customer the same day. This is where I came in! At Ouway, I was the Head of Automation & Data Crawling which meant my primary focus was to scrape data from various fashion sites and populate the Ouway app with the latest products. This was a challenging task, as each site had a different structure and required a unique approach to scrape the required data. However, I was able to successfully automate the process and ensure that the app was always up-to-date with the latest products available for same-day delivery.

API Wrappers

One method, if readable was to use the store’s internal API. If the store had a public API, most of the data contained product inventory and location data, which I would then transform for our backend to accept and populate the Ouway app. In this blogpost, I’m going go over one of the API wrappers I wrote for Best Buy.

Best Buy API

The Best Buy API provides product data, including pricing, availability, and reviews. The raw JSON data from Best Buy is transformed into an object with attributes that can be utilized. This library streamlines the API call process by taking care of query structure, GET requests, and JSON parsing in the background.

To use the Best Buy API, I wrote a Python wrapper that makes it easy to retrieve data from the API and transform it into a format that can be used by our backend. This wrapper made it possible for us to quickly add Best Buy products to the Ouway app and ensure that our customers had access to the latest products available for same-day delivery. Here’s a closer look into the wrapper:

Data Modeling

This file contains the classes that represent the objects returned by the Best Buy API. These classes define the attributes that are returned by the API and provide a convenient way to access them.

class Product:
    def __init__(self, json) -> str:
				self.sku = json.get('sku', None)
        self.score = json.get('score', None)
        self.productId = json.get('productId', None)
        self.name = json.get('name', None)
        self.source = json.get('source', None)
        self.type = json.get('type', None)
        self.startDate = json.get('startDate', None)
        self.new = json.get('new', None)        self.name = data['name']
        self.description = data['longDescription']
        self.price = data['regularPrice']
        self.image_url = data['image']

This is an example of the Product class, which represents a product returned by the Best Buy API. The class constructor takes the raw json of data as an argument and initializes the attributes of the object. Using these classes, we can easily access the data returned by the Best Buy API and transform it into a format that can be used by our backend.

Backend

In the backend, we have a _request function that makes a request to the Best Buy API. This function takes a query and a category as arguments and returns a JSON response. We also have a ProductAPI class, along with the other API options Best Buy offers, that has several methods for searching the Best Buy product catalog based on different criteria. These methods make use of the _request function to retrieve data from the Best Buy API.

def _request(query, category, sort=None):
    """
    Makes request to Best Buy API

    Args:
        category (str): Refers to different API categories
        query (str): Query for the specific APIs
        sort (str): None

    Returns:
        str: JSON response of request

    """
    return requests.get(
        'https://api.bestbuy.com/v1/{category}{query}?apiKey={key}{sort}&format=json'.format(
            category=category, query=query, key=api_key, sort=(
                sort if sort else ""))).json()

The wrapper contained two main components, the Product class and the ProductAPI class. The Product class represented a product returned by the Best Buy API and defined the attributes that were returned by the API. The ProductAPI class had several methods for searching the Best Buy product catalog based on different criteria. These methods made use of the _request function to retrieve data from the Best Buy API.

The _request function made a request to the Best Buy API and returned a JSON response. It took a query and a category as arguments and was able to retrieve data from the Best Buy API based on these arguments. This function was important because it took care of query structure, GET requests, and JSON parsing in the background, making it easy to retrieve data from the Best Buy API.

class ProductAPI:

    def _query(self, query, sort=None):
        """
        Private function to call API

        Args:
            query (str): Query for API

        Returns:
            list: Either a single or list of Product object(s)
        """
        product_list = _request(
            query,
            'products',
            '&sort={0}.asc'.format(sort) if sort else None).get(
            'products',
            [])
        return [Product(product) for product in product_list]

    def search(self, keyword=None, **kwargs):
        """
        Search Best Buy Product catalog based on search Keyword(s) and product attributes

        Args:
            keyword (str): search element
            **kwargs (str): key, value pair (product attribute, search (any))

        Returns:
            list: List of all products based on criteria
        """

        if not keyword:
            keyword = ""
        else:
            keyword = '(search={})'.format(keyword)
        if kwargs:
            keyword += '&'
            for key, value in kwargs.items():
                keyword = '{s}{k}={v}&'.format(s=keyword, k=key, v=value)
            keyword = '({})'.format(keyword[:-1])
        return self._query(keyword)

    def search_sku(self, sku, sort=None):
        """
        Search Best Buy Product catalog based on sku

        Args:
            sku (str): search element

        Returns:
            object: Singular Product object
        """

        try:
            return self._query('(sku={0})'.format(str(sku)), sort)[0]
        except IndexError:
            return None

    def search_upc(self, upc, sort=None):
        """
        Search Best Buy Product catalog based on upc

        Args:
            upc (str): search element

        Returns:
            object: Singular Product object
        """

        try:
            return self._query('(upc={0})'.format(str(upc)), sort)[0]
        except IndexError:
            return None

    def search_description(self, description, sort=None):
        """
        Search Best Buy Product catalog based on description

        Args:
            description (str): search element

        Returns:
            object: Singular Product object
        """
        return self._query('(description={0})'.format(description), sort=None)

Using API wrappers like this one allowed us to quickly and easily populate the Ouway app with the latest products available for same-day delivery, and ensured that our customers always had access to the most up-to-date information.

Conclusion

Though my time at Ouway was not as long as I first anticipated, it was an important stepping stone in my career and I will always be grateful for the experience. I am eagerly looking forward to continuing this project in the near future. I have already begun brainstorming new ideas and exploring different approaches that could potentially take this project to the next level. Overall, I am confident that this project has a lot of potential and I am excited to see where it goes in the coming months.