Fashion, NYC, API Wrappers
2023-04-30

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.


_13
class Product:
_13
def __init__(self, json) -> str:
_13
self.sku = json.get('sku', None)
_13
self.score = json.get('score', None)
_13
self.productId = json.get('productId', None)
_13
self.name = json.get('name', None)
_13
self.source = json.get('source', None)
_13
self.type = json.get('type', None)
_13
self.startDate = json.get('startDate', None)
_13
self.new = json.get('new', None) self.name = data['name']
_13
self.description = data['longDescription']
_13
self.price = data['regularPrice']
_13
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.


_17
def _request(query, category, sort=None):
_17
"""
_17
Makes request to Best Buy API
_17
_17
Args:
_17
category (str): Refers to different API categories
_17
query (str): Query for the specific APIs
_17
sort (str): None
_17
_17
Returns:
_17
str: JSON response of request
_17
_17
"""
_17
return requests.get(
_17
'https://api.bestbuy.com/v1/{category}{query}?apiKey={key}{sort}&format=json'.format(
_17
category=category, query=query, key=api_key, sort=(
_17
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.


_86
class ProductAPI:
_86
_86
def _query(self, query, sort=None):
_86
"""
_86
Private function to call API
_86
_86
Args:
_86
query (str): Query for API
_86
_86
Returns:
_86
list: Either a single or list of Product object(s)
_86
"""
_86
product_list = _request(
_86
query,
_86
'products',
_86
'&sort={0}.asc'.format(sort) if sort else None).get(
_86
'products',
_86
[])
_86
return [Product(product) for product in product_list]
_86
_86
def search(self, keyword=None, **kwargs):
_86
"""
_86
Search Best Buy Product catalog based on search Keyword(s) and product attributes
_86
_86
Args:
_86
keyword (str): search element
_86
**kwargs (str): key, value pair (product attribute, search (any))
_86
_86
Returns:
_86
list: List of all products based on criteria
_86
"""
_86
_86
if not keyword:
_86
keyword = ""
_86
else:
_86
keyword = '(search={})'.format(keyword)
_86
if kwargs:
_86
keyword += '&'
_86
for key, value in kwargs.items():
_86
keyword = '{s}{k}={v}&'.format(s=keyword, k=key, v=value)
_86
keyword = '({})'.format(keyword[:-1])
_86
return self._query(keyword)
_86
_86
def search_sku(self, sku, sort=None):
_86
"""
_86
Search Best Buy Product catalog based on sku
_86
_86
Args:
_86
sku (str): search element
_86
_86
Returns:
_86
object: Singular Product object
_86
"""
_86
_86
try:
_86
return self._query('(sku={0})'.format(str(sku)), sort)[0]
_86
except IndexError:
_86
return None
_86
_86
def search_upc(self, upc, sort=None):
_86
"""
_86
Search Best Buy Product catalog based on upc
_86
_86
Args:
_86
upc (str): search element
_86
_86
Returns:
_86
object: Singular Product object
_86
"""
_86
_86
try:
_86
return self._query('(upc={0})'.format(str(upc)), sort)[0]
_86
except IndexError:
_86
return None
_86
_86
def search_description(self, description, sort=None):
_86
"""
_86
Search Best Buy Product catalog based on description
_86
_86
Args:
_86
description (str): search element
_86
_86
Returns:
_86
object: Singular Product object
_86
"""
_86
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.