93 Case Study: Product & Shopping Cart API
93 Case Study: Product & Shopping Cart API
In the world of e-commerce application development, APIs for products and shopping carts are two fundamental components that often serve as the basis for integration as well as for building more advanced features. This time, I want to share an interesting case study I worked on—an internal project we codenamed 93—focusing on the design, implementation, and simulation of how product and shopping cart APIs are used.
This article covers common challenges, measurable solutions, and code examples that I hope will provide a practical perspective for fellow engineers who are building similar systems.
Part 1: Challenges of the Product & Shopping Cart API
Before getting to the solutions, let’s identify some of the most common challenges:
- Data Consistency: A product’s price, stock, or promo status changes after the user has already added it to their cart.
- Atomicity & Concurrent Updates: Race conditions arise when many carts are updated at the same time.
- Real-time Experience: Changes to products/carts need to be reflected with minimal delay.
- Scalability: Hundreds of thousands of users accessing the API simultaneously.
Part 2: Data Model
Simple Entity Diagram (ERD)
erDiagram
USER ||--o{ CART : owns
CART ||--|{ CART_ITEM : contains
PRODUCT ||--o{ CART_ITEM : referenced
USER {
string id
string email
}
CART {
string id
string user_id
}
CART_ITEM {
string id
string cart_id
string product_id
int quantity
float price_at_added
}
PRODUCT {
string id
string name
float price
int stock
bool enabled
}
Notice the price_at_added field on CART_ITEM. It stores the product’s price at the moment the user adds it to the cart, so any subsequent price change does not automatically affect existing items. This helps mitigate potential price conflicts.
Part 3: API Design
1. Product Endpoint
Example:GET /api/products?limit=10&offset=0&search=jaket
Response:
1{
2 "data": [
3 {
4 "id": "P001",
5 "name": "Jaket Waterproof",
6 "price": 299000,
7 "stock": 12,
8 "enabled": true
9 },
10 // ...
11 ],
12 "meta": {
13 "total": 120,
14 "limit": 10,
15 "offset": 0
16 }
17}2. Shopping Cart
Base endpoints:
GET /api/cart- View the user’s cart contentsPOST /api/cart/items- Add a product to the cartPATCH /api/cart/items/{itemId}- Change quantityDELETE /api/cart/items/{itemId}- Remove from the cart
Example request to add an item:
1POST /api/cart/items
2Content-Type: application/json
3
4{
5 "product_id": "P001",
6 "quantity": 2
7}Example response:
1{
2 "success": true,
3 "cart": {
4 "items": [
5 {
6 "item_id": "CI01",
7 "product_id": "P001",
8 "product_name": "Jaket Waterproof",
9 "quantity": 2,
10 "price_at_added": 299000
11 }
12 ]
13 }
14}Part 4: Simulating Important Scenarios
1. Price Changes After the Item Is in the Cart
- Case: User A adds a jacket priced at 299,000 to their cart. By the time they check out, the jacket’s price in the catalog has risen to 349,000.
- Solution: The price on the cart item stays at 299,000, in line with the history (
price_at_added).
If the promo has expired, the promo status is verified at checkout before the order is actually finalized.
2. Updates & Stock Consistency
- When a user adds a product to the cart:
- The system does not immediately decrement the stock.
- When
POST /api/order(checkout) is called, stock validation is performed. - If there isn’t enough stock, an error response is returned and the user can update their cart contents.
Checkout Response Simulation Table
| Scenario | Response | Explanation |
|---|---|---|
| Sufficient Stock | Success | The transaction proceeds, and stock is reduced according to the quantity. |
| Insufficient Stock | Fail | An error message is returned, prompting the user to update/refresh the cart. Example: “Sorry, only 1 left in stock, please adjust the quantity.” |
| Product Price Changed | Success/Warning | If the system locks the price at add-to-cart, a success message is returned. If not, the server responds with the price update and asks the user to re-validate. |
Part 5: Simple Code Example (Node.js pseudo code)
1. Add Item to Cart
1// add item to cart
2async function addItemToCart(userId, productId, qty) {
3 const product = await db.products.findById(productId);
4 if (!product || !product.enabled) throw new Error('Product not available');
5
6 const cart = await db.carts.findOrCreateByUserId(userId);
7
8 // Query the current price/stock
9 const item = {
10 cart_id: cart.id,
11 product_id: productId,
12 quantity: qty,
13 price_at_added: product.price, // Store the price at this moment
14 };
15 // Upsert (update if it already exists, insert if not)
16 await db.cart_items.upsert(item);
17 return item;
18}2. Checkout Simulation with Validation
1async function checkout(userId) {
2 const cart = await db.carts.findByUserId(userId);
3 for (const item of cart.items) {
4 const product = await db.products.findById(item.product_id);
5 if (product.stock < item.quantity) {
6 throw new Error(`Stok ${product.name} tinggal ${product.stock}`);
7 }
8 }
9 // Deduct stock, create order, etc.
10 // ...
11 return { status: 'success' };
12}Part 6: Checkout Flow Diagram
flowchart TD
A[User klik checkout] --> B{Loop: Semua item di cart}
B --> |Stok cukup| C
C --> D{Item berikutnya?}
D -->|Ya|B
D -->|Tidak| E[Bikin order & kurangi stok]
B --> |Stok kurang| F[Gagal: minta user ubah cart]
The flow diagram above ensures the atomicity of the entire process: if a single item fails, the process is aborted before the order is created.
Part 7: Key Takeaways
- Store the price at add-to-cart time. This safeguards price consistency even when the catalog changes.
- Check stock only at checkout, to avoid race condition issues while also providing a smoother shopping experience.
- Add plenty of server-side validation: never trust data from the frontend alone.
- Implement an atomic process. Ensure all changes (order, stock deduction) happen within a single transaction.
Conclusion
In case study “93,” a solid and scalable product & shopping cart API design is an essential foundation for accommodating more e-commerce features, from promos and wishlists to loyalty points. With separation of concerns, a robust data model, and validation at the API level, the system you build is able to handle business dynamics as well as large-scale traffic.
I hope the breakdown and simulations above are useful, both for juniors who are just starting out and for senior engineers tinkering with scalability!
See you in the next case study. 🚀