Skip to content

The marketplace API is backend application that contains the basic functionality necessary for an eCommerce website.

Notifications You must be signed in to change notification settings

enochtangg/marketplace_api

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

27 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

marketplace_api

Introduction

The marketplace API is backend application that contains the basic functionality necessary for an eCommerce website. For example, a user may

  • sign-up
  • login
  • query products from the store (either one at a time or all at once)
  • add items to their cart
  • remove items from their cart
  • view the metadata in their cart (owner, subtotal, total, number of items, and all carted items)
  • checkout their cart

Technical Overview

This repository contains the code for the market_api application written in Node.js. The application ultizes GraphQL which is a query lanuage for the API that alllows the user to clearly and easily define what type of data they want to receive. As for storing persistent data, the application uses a MySQL instance and uses Sequelize as a ORM tool to interact with the database.

Security

The API is secured by utilizing JWT. When a user signs-up or login, the API will automatically create Cart instance in the database and will return JWT that contains the data for that cart. The user may now include this JWT as a Bearer Token in their request headers and will have access to modifying their cart. This allows users to signup or login (using a username & password) into their own cart. Users will not have access to each other's carts unless they somehow get a user's JWT (or username & password).

How does the Authorization work within the server? Since GraphQL has only one endpoint, which all the requests are made through, I simply made a authorization middleware to that endpoint. Just as with REST, the JWT will check if an Authorization header with a valid token is available on every request made to the endpoint. If so, then we decode it a User object (which is actually just a Cart object) to the requet. Then, I simply added an authorization check at the resolvers I wanted to secure, and boom! Each endpoint is only accessible to the right users with valid credentials.

Database Schema

I look inspiration from OOP design principles when designing the layout of the database schema. Essentially, we have a Cart entity that stores all the information from the user. In the future, I would go even further to and make Cart and User entities separate from each other. The reason for this is because in a eCommerce website (Shopify), you would typically have a account (which would store all the User metadata) and a it's respective Cart which would store all the its metadata as well as CartItems data.

In addition, we also have Product entity that hold all the product metadata and a CartItem entity which is created when a user adds an item to cart. The reason why we need a new CartItem object is because we need entity to carry the foreign keys of both the cartId and productId. By doing this, we have a link that tells us which items have been added to which cart.

db_schema

API Usage

Types

You can include any of these attributes in your API query to receive back the exact data you asked for.

Product { id: Int, title: String, price: Float, inventoryCount: Int }

CartItem { id: Int, cartId: Int, productId: Int, productTitle: String, productPrice: Float, quantity: Int }

Cart { id: Int, owner: String, subtotal: Float, total: Float, numberOfItems: Int, cartedItems: [CartItem]}

Product Queries

Method Description Params Return Type Requires JWT
getOneProduct() Returns the product with the corresponding productId id: Int! Product No
getAllProducts() Returns an array of all products. If boolean onlyAvailableInventory is true, it will only return all the products with available inventory (where inventoryCount != 0) onlyAvailableInventory: Boolean! [Product] No

Cart Queries

Method Description Params Return Type Requires JWT
getCart Returns the corresponding cart with its respective JWT None Cart Yes

Cart Mutations

Method Description Params Return Type Requires JWT
login() Returns a JWT with the associated cart metadata associated owner: String!, password: String! String (JWT) No
signup() Creates a new cart entity and returns a JWT with the associated cart metadata associated owner: String!, password: String! String (JWT) No
addItemToCart() Searches for the productId and adds it into cart productId: Int!, quantity: Int! Cart Yes
removeItemFromCart() Searches for product in cart and removes it productId: Int! Cart Yes
checkoutCart Checks out the cart by validating and decrementing the inventoryCount of all the cartItem in the cart None String Yes

Get Started (Setting Up Locally)

Setting up marketplace_api is pretty simple. The two main compontents of this application is the backend server and the database server. The following steps will help you run the server locally, run a MySQL instance locally, and use Insomnia for endpoint testing.

  1. Clone this repository.
git clone https://github.com/enochtangg/marketplace_api.git
  1. Change into marketplace_api directory
cd marketplace_api
  1. Install dependencies from npm
npm install
  1. Make sure you have MySQL install into your machine. You can check by using:
mysql -V
  1. If you do not have MySQL installed, you can install it using homebrew by following the steps here.

  2. Once MySQL is installed, you want to set the root password for your local instance. For testing purposes, set password to 'password'. If you want create your own password, remember to change the DATABASE_PASSWORD variable in the .env file of this repository.

brew services start mysql
mysql -u root
mysql> CREATE DATABASE marketplace;
mysql> USE mysql;
mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY 'password';
mysql> FLUSH PRIVILEGES;
mysql> quit

  1. Lastly, run the server. Once the server starts, it will execute a bunch of raw queries to populate the Products table so you may play around with the carting items.
npm start

Demo

To test and play around with the API, I recommend using the Insomnia to make requests. Insomnia is essentially Postman, but for GraphQL. Install here. You could also use to built in graphiql interfact at http://localhost:9000/graphql but that does not allow you to add a JWT into the Bearer Token Header so you wont be able to access the bulk of the endpoints.

  1. Creating a new request named "Shopify Challenge Test" that is a POST request and takes in a GraphQL as body. demo1

  2. Querying all products in the store. Notice how we did not need any authentication to query products. demo2

  3. Notice how once we call a endpoint that requires a JWT, we get a unauthorized error. demo3

  4. Using the signup endpoint to create a user (cart) with a owner name: "Shopify Test" and a password: "password". Notice how the server returned a JWT to us. demo4

  5. Heading over to the Auth tab and pasting JWT as a Token Bearer for all our prior requests. demo5

  6. Added an item to our cart. Here I added the item with productId "2" which was a "binder". I can also specify how many I want to add as quantity of "10". Notice, that the response automatically updates my subtotal, total and numberOfItems in my cart as well as an array of all my carted items. I can also remove items from my cart with the removeItemFromCart() endpoint. demo6

  7. If you look at the first screenshot when we queried all the items. The product "binder" only had 10 avaible in its inventoryCount and I have added 10 in my cart. So, when I try to checkout, I get an error saying that the product is sold out. demo7

  8. Let's try again with these items. demo8

  9. Since there is enough items in stock for us to purchase, we get a success message! demo9

  10. Notice how when we query the products again, the inventoryCount of the products we just checked out has been decremented by the quantity we purchased. demo10

Error Handling

I have implemented more custom error handling (eg. trying to cart an item that doesn't exist, logging in with wrong password, and etc.) which can all be found in the error.js file.

Next Steps

Although this project is for a Shopify Challenge to replicate the backbones the backend API of a eCommerce website, some future steps I would like to incorporate is the following:

  • Containerize this application with Docker for deployment in any cloud service
  • Split Cart entity into Carts and Users
  • Add more functionality to this API
  • If this was a real world application, I might think about splitting this into microservices (eg. have a service to handle user authentication, service to handle querying products, and etc.)
  • Again if this was a real world application, think about how we would scale this application (distributed databases with replication or partitioning, handling concurrent queries to database, etc.)
  • Write test cases

About

The marketplace API is backend application that contains the basic functionality necessary for an eCommerce website.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published