ecommerce_api

command module
v0.0.0-...-af2f739 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 15, 2025 License: MIT Imports: 15 Imported by: 0

README

E-Commerce API

The E-Commerce API is a secure backend service that manages orders, products, and user authentication. It includes an inventory management API to coordinate stock, trigger replenishment, and maintain a dashboard of the operational state. Its business goal is to keep product stock above safety thresholds, streamline purchase orders, and sync aggregates for customer ordering.


Features

  • User Authentication:

    • Register users with hashed passwords.
    • Login functionality with JWT-based authentication.
    • Role-based access control (e.g., admin-only routes).
  • Product Management:

    • Create, read, update, and delete (CRUD) operations for products.
    • Admin-only access for creating, updating, and deleting products.
    • Manage product-level reorder thresholds that act as global fallbacks for automatic replenishment.
  • Order Management:

    • Place and retrieve user orders.
    • Admin-only functionality for updating order status.
  • Inventory & Procurement:

    • Manage suppliers, warehouses, and per-location stock levels.
    • Automatically trigger purchase orders when warehouse stock drops below thresholds.
    • Track purchase order lifecycles, including receiving goods back into inventory.
  • Swagger API Documentation:

    • Auto-generated and interactive documentation for easy API testing.
  • Rate Limiting:

    • Per-client rate limiting to prevent abuse.
  • CORS Middleware:

    • Protects the API from cross-origin requests while allowing specified domains.

Table of Contents


Installation

Prerequisites
  • Go (v1.18+)
  • PostgreSQL (v14+)
Setup
  1. Clone the repository:

    git clone https://github.com/TobiAdeniji94/ecommerce_api.git
    cd ecommerce_api
    
  2. Install dependencies:

    go mod tidy
    
  3. Set up environment variables by creating a .env file:

    touch .env
    
  4. Populate the .env file (see Environment Variables).

  5. Run the project:

    go run main.go
    
Hot Reloading (optional)

For a smoother development loop install Air and let it rebuild on file changes:

go install github.com/cosmtrek/air@latest
air

The bundled .air.toml configuration compiles to ./tmp/server and restarts the API automatically whenever Go files change.

  1. Access the application:

Containerized Setup

Spin up PostgreSQL, the Go API, and the frontend dashboard together using Docker Compose:

docker compose up --build

Exposed services:

Environment variables such as JWT_SECRET and DB_* are configured in docker-compose.yml. Tweak them before deployment if necessary.

Containerized Development with Hot Reload

To mount the source tree and run the API with Air inside Docker:

docker compose --profile dev up --build db api-dev frontend

The api-dev service watches for file changes and rebuilds automatically, mirroring the local air workflow.

Health Checks
  • API: GET /healthz returns 200 when the database is reachable.
  • Database: Compose uses pg_isready to ensure Postgres is accepting connections before the API starts.

Frontend Dashboard

The repository ships with a lightweight dashboard under frontend/ that consumes the REST API.

  1. Install a simple static file server (for example, npm install -g serve).
  2. Run the dashboard on port 3000 (permitted by the backend CORS policy):
    serve frontend -l 3000
    
  3. Visit http://localhost:3000 and provide:
    • API base URL (e.g. http://localhost:3001/api/v1/)
    • A JWT token (use an admin token to unlock write operations)
  4. Use the interface to inspect product stock, adjust warehouse inventory, trigger reorder scans, and update purchase orders.

API Documentation

The Swagger UI is available at https://ecommerce-api-vkui.onrender.com/swagger/index.html.

User Management

Register a New User
  • Method: POST
  • Route: /api/v1/users/register
  • Description: Register a new user with email and password.
  • Access: Public
Request Payload:
{
  "email": "user@example.com",
  "password": "securepassword"
}
Response:
  • Success (200):
    {
      "message": "User registered successfully",
      "data": {
        "user_id": "uuid-1234-5678-91011"
      }
    }
    
  • Validation Error (400):
    {
      "errors": [
        {
          "field": "email",
          "message": "Email is required"
        },
        {
          "field": "password",
          "message": "Password is required"
        }
      ]
    }
    

Login
  • Method: POST
  • Route: /api/v1/users/login
  • Description: Authenticate a user and issue a JWT for session management.
  • Access: Public
Request Payload:
{
  "email": "user@example.com",
  "password": "securepassword"
}
Response:
  • Success (200):
    {
      "message": "Login successful",
      "data": {
        "token": "jwt-token",
        "user_id": "uuid-1234-5678-91011"
      }
    }
    
  • Invalid Credentials (401):
    {
      "message": "Invalid email or password"
    }
    

Product Management (Admin Privileges Required)

Create a Product
  • Method: POST
  • Route: /api/v1/products
  • Description: Create a new product.
  • Access: Admin only
Request Payload:
{
  "name": "Wireless Mouse",
  "description": "Ergonomic wireless mouse with adjustable DPI",
  "price": 19.99,
  "stock": 100
}
Response:
  • Success (200):
    {
      "message": "Product created successfully",
      "data": {
        "id": "uuid-1234-5678-91011",
        "name": "Wireless Mouse",
        "description": "Ergonomic wireless mouse with adjustable DPI",
        "price": 19.99,
        "stock": 100,
        "created_at": "2024-12-25T10:00:00Z"
      }
    }
    

List All Products
  • Method: GET
  • Route: /api/v1/products
  • Description: Retrieve a list of all available products.
  • Access: Authenticated users
Response:
  • Success (200):
    {
      "message": "Products retrieved successfully",
      "data": [
        {
          "id": "uuid-1234-5678-91011",
          "name": "Wireless Mouse",
          "description": "Ergonomic wireless mouse with adjustable DPI",
          "price": 19.99,
          "stock": 100
        }
      ]
    }
    

Get a Product by ID
  • Method: GET
  • Route: /api/v1/products/{id}
  • Description: Retrieve details of a specific product by its ID.
  • Access: Authenticated users
Response:
  • Success (200):
    {
      "message": "Product retrieved successfully",
      "data": {
        "id": "uuid-1234-5678-91011",
        "name": "Wireless Mouse",
        "description": "Ergonomic wireless mouse with adjustable DPI",
        "price": 19.99,
        "stock": 100
      }
    }
    

Update a Product
  • Method: PUT
  • Route: /api/v1/products/{id}
  • Description: Update details of an existing product by its ID.
  • Access: Admin only
Request Payload:
{
  "name": "Updated Wireless Mouse",
  "description": "Updated ergonomic wireless mouse",
  "price": 24.99,
  "stock": 150
}
Response:
  • Success (200):
    {
      "message": "Product updated successfully",
      "data": {
        "id": "uuid-1234-5678-91011",
        "name": "Updated Wireless Mouse",
        "description": "Updated ergonomic wireless mouse",
        "price": 24.99,
        "stock": 150
      }
    }
    

Delete a Product
  • Method: DELETE
  • Route: /api/v1/products/{id}
  • Description: Delete a product by its ID.
  • Access: Admin only
Response:
  • Success (200):
    {
      "message": "Product deleted successfully"
    }
    

Order Management

Place an Order
  • Method: POST
  • Route: /api/v1/orders
  • Description: Place an order for one or more products.
  • Access: Authenticated users
Request Payload:
{
  "items": [
    {
      "product_id": "uuid-1234-5678-91011",
      "quantity": 2
    }
  ]
}
Response:
  • Success (200):
    {
      "message": "Order placed successfully",
      "data": {
        "order_id": "uuid-1234-5678-91011",
        "status": "Pending",
        "items": [
          {
            "product_id": "uuid-1234-5678-91011",
            "quantity": 2
          }
        ]
      }
    }
    

List All Orders for a User
  • Method: GET
  • Route: /api/v1/orders
  • Description: List all orders placed by the authenticated user.
  • Access: Authenticated users
Response:
  • Success (200):
    {
      "message": "Orders retrieved successfully",
      "data": [
        {
          "order_id": "uuid-1234-5678-91011",
          "status": "Pending",
          "items": [
            {
              "product_id": "uuid-1234-5678-91011",
              "quantity": 2
            }
          ]
        }
      ]
    }
    

Cancel an Order
  • Method: PUT
  • Route: /api/v1/orders/{id}/cancel
  • Description: Cancel an order if it is still in the "Pending" status.
  • Access: Authenticated users
Response:
  • Success (200):
    {
      "message": "Order canceled successfully",
      "data": {
        "order_id": "uuid-1234-5678-91011",
        "status": "Canceled"
      }
    }
    

Update Order Status
  • Method: PUT
  • Route: /api/v1/orders/{id}/status
  • Description: Update the status of an order.
  • Access: Admin only
Request Payload:
{
  "status": "Shipped"
}
Response:
  • Success (200):
    {
      "message": "Order status updated successfully",
      "data": {
        "order_id": "uuid-1234-5678-91011",
        "status": "Shipped"
      }
    }
    

Inventory Management

  • List Warehouse Inventory
    • GET /api/v1/inventory
    • Optional warehouse_id and product_id query parameters filter the response.
    • Response
      {
        "message": "Inventory retrieved successfully",
        "data": [
          {
            "warehouse_id": "uuid-warehouse",
            "product_id": "uuid-product",
            "quantity_on_hand": 12,
            "reorder_threshold": 15,
            "preferred_supplier_id": "uuid-supplier",
            "warehouse": { "id": "uuid-warehouse", "name": "North Hub" },
            "product": { "id": "uuid-product", "name": "Bluetooth Speaker", "reorder_threshold": 20 },
            "preferred_supplier": { "id": "uuid-supplier", "name": "Acme Components" }
          }
        ]
      }
      
  • Create or Update Inventory
    • POST /api/v1/inventory
    • Admin-only; upserts quantity, thresholds, and preferred suppliers for a warehouse/product pair.
    • Request
      {
        "warehouse_id": "uuid-warehouse",
        "product_id": "uuid-product",
        "quantity_on_hand": 12,
        "reorder_threshold": 15,
        "preferred_supplier_id": "uuid-supplier"
      }
      
    • Response
      {
        "message": "Warehouse inventory upserted successfully",
        "data": {
          "warehouse_id": "uuid-warehouse",
          "product_id": "uuid-product",
          "quantity_on_hand": 12,
          "reorder_threshold": 15
        }
      }
      
  • Adjust Inventory Levels
    • POST /api/v1/inventory/{warehouseId}/{productId}/adjust
    • Admin-only; increments or decrements on-hand quantity.
    • Request
      { "quantity_delta": -2 }
      
    • Response
      {
        "message": "Inventory adjusted successfully",
        "data": {
          "warehouse_id": "uuid-warehouse",
          "product_id": "uuid-product",
          "quantity_on_hand": 10
        }
      }
      
  • Update Reorder Threshold
    • PUT /api/v1/inventory/{warehouseId}/{productId}/threshold
    • Admin-only; updates the threshold that triggers replenishment.
    • Request
      { "reorder_threshold": 18 }
      
  • Trigger Reorder Scan
    • POST /api/v1/inventory/reorder-scan
    • Admin-only; performs an automated scan to create purchase orders when inventory is low. Warehouse-specific thresholds take precedence, but the product-level reorder_threshold provides a global fallback.
    • Request
      { "dry_run": true }
      
    • Response
      {
        "message": "Reorder scan completed",
        "data": [
          {
            "warehouse_id": "uuid-warehouse",
            "product_id": "uuid-product",
            "action": "dry_run",
            "quantity_ordered": 8,
            "supplier_id": "uuid-supplier",
            "lead_time_hours": 48
          }
        ]
      }
      

Warehouse Management

  • List Warehouses
    • GET /api/v1/warehouses
    • Optional include_inventory=true query parameter embeds inventory details and utilisation.
    • Response
      {
        "message": "Warehouse(s) retrieved successfully",
        "data": [
          {
            "id": "uuid-warehouse",
            "name": "North Hub",
            "location": "Manchester, UK",
            "capacity": 200,
            "current_utilisation": 18
          }
        ]
      }
      
  • Create Warehouse
    • POST /api/v1/warehouses (admin only).
    • Request
      {
        "name": "South Hub",
        "location": "London, UK",
        "capacity": 150
      }
      
  • Get Warehouse
    • GET /api/v1/warehouses/{id}
    • Response
      {
        "message": "Warehouse retrieved successfully",
        "data": {
          "id": "uuid-warehouse",
          "name": "North Hub",
          "capacity": 200,
          "inventory": [
            {
              "product_id": "uuid-product",
              "quantity_on_hand": 12,
              "reorder_threshold": 15
            }
          ]
        }
      }
      
  • Update Warehouse
    • PUT /api/v1/warehouses/{id} (admin only).
    • Request
      {
        "name": "North Hub",
        "location": "Manchester, UK",
        "capacity": 220
      }
      
  • Delete Warehouse
    • DELETE /api/v1/warehouses/{id} (admin only).

Supplier Management

  • List Suppliers
    • GET /api/v1/suppliers
    • Response
      {
        "message": "Supplier(s) retrieved successfully",
        "data": [
          {
            "id": "uuid-supplier",
            "name": "Acme Components",
            "contact_info": "acme@example.com",
            "default_lead_time_hours": 48
          }
        ]
      }
      
  • Create Supplier
    • POST /api/v1/suppliers (admin only).
    • Request
      {
        "name": "Widget Wholesale",
        "contact_info": "widgets@example.com",
        "default_lead_time_hours": 72
      }
      
  • Get Supplier
    • GET /api/v1/suppliers/{id}
    • Response
      {
        "message": "Supplier retrieved successfully",
        "data": {
          "id": "uuid-supplier",
          "name": "Widget Wholesale",
          "contact_info": "widgets@example.com",
          "default_lead_time_hours": 72
        }
      }
      
  • Update Supplier
    • PUT /api/v1/suppliers/{id} (admin only).
    • Request
      {
        "name": "Widget Wholesale",
        "contact_info": "widgets@example.com",
        "default_lead_time_hours": 60
      }
      
  • Delete Supplier
    • DELETE /api/v1/suppliers/{id} (admin only).

Purchase Order Management

  • List Purchase Orders
    • GET /api/v1/purchase-orders (admin only) with optional status, warehouse_id, and supplier_id filters.
    • Response
      {
        "message": "Purchase order(s) retrieved successfully",
        "data": [
          {
            "id": "uuid-po",
            "product_id": "uuid-product",
            "warehouse_id": "uuid-warehouse",
            "supplier_id": "uuid-supplier",
            "quantity_ordered": 10,
            "status": "Pending",
            "expected_arrival_date": "2025-10-16T12:00:00Z"
          }
        ]
      }
      
  • Create Purchase Order
    • POST /api/v1/purchase-orders (admin only) to manually request stock from a supplier.
    • Request
      {
        "product_id": "uuid-product",
        "warehouse_id": "uuid-warehouse",
        "supplier_id": "uuid-supplier",
        "quantity_ordered": 10
      }
      
  • Get Purchase Order
    • GET /api/v1/purchase-orders/{id} (admin only).
  • Update Purchase Order Status
    • PUT /api/v1/purchase-orders/{id}/status (admin only); receiving an order automatically increases warehouse stock and recalculates product availability.
    • Request
      { "status": "Received" }
      

Environment Variables

Create a .env file in the root directory with the following variables:

DB_HOST=
DB_USER=
DB_PASSWORD=
DB_NAME=
DB_PORT=
DB_SSLMODE=require
JWT_SECRET=
PORT=3001

DB_SSLMODE defaults to require. Set it to disable when connecting to local/Postgres containers that do not provide TLS.


Database Schema

users Table
Column Type Description
id UUID Primary key
email VARCHAR(255) Unique user email
password VARCHAR(255) Hashed password
role VARCHAR(50) User role (default: user)
created_at TIMESTAMP Timestamp of creation
products Table
Column Type Description
id UUID Primary key
name VARCHAR(255) Product name
description TEXT Product description
price FLOAT Price of the product
stock INT Available stock (aggregated across warehouses)
reorder_threshold INT Global fallback threshold for automatic reorders
created_at TIMESTAMP Timestamp of creation
suppliers Table
Column Type Description
id UUID Primary key
name VARCHAR(255) Supplier name
contact_info TEXT Contact details
default_lead_time_hours INT Default restock lead time
created_at TIMESTAMP Timestamp of creation
warehouses Table
Column Type Description
id UUID Primary key
name VARCHAR(255) Warehouse label
location VARCHAR(255) Optional address/location
capacity INT Maximum units stored
created_at TIMESTAMP Timestamp of creation
warehouse_inventory Table
Column Type Description
warehouse_id UUID Part of composite primary key, references warehouses.id
product_id UUID Part of composite primary key, references products.id
quantity_on_hand INT Units currently stored
reorder_threshold INT Warehouse-specific threshold (fallback to product if zero)
preferred_supplier_id UUID Optional supplier override
created_at TIMESTAMP Timestamp of creation
orders Table
Column Type Description
id UUID Primary key
user_id UUID Foreign key to users table
status VARCHAR(50) Order status (default: Pending)
created_at TIMESTAMP Timestamp of creation
order_items Table
Column Type Description
id UUID Primary key
order_id UUID Foreign key to orders.id
product_id UUID Foreign key to products.id
warehouse_id UUID Warehouse that fulfilled the line (nullable)
quantity INT Units ordered
purchase_orders Table
Column Type Description
id UUID Primary key
product_id UUID Product to restock
warehouse_id UUID Warehouse receiving the stock
supplier_id UUID Supplier fulfilling the order
quantity_ordered INT Units requested
status VARCHAR(20) Pending/Submitted/Received/Cancelled
order_date TIMESTAMP Time purchase order was created
expected_arrival_date TIMESTAMP Estimated arrival time
received_date TIMESTAMP (nullable) When stock was confirmed received

How It Works

  1. User Authentication
    Passwords are hashed with bcrypt and JWT tokens carry the user id/role, enabling admin-only routes.

  2. Product Management
    Admins maintain product catalogue data, including the global reorder_threshold that feeds the replenishment engine. Public consumers can list and read products.

  3. Warehouse & Inventory Tracking
    Each warehouse records capacity and per-product on-hand quantities. Warehouse-level thresholds and preferred suppliers override product defaults when present.

  4. Automatic Reordering & Purchase Orders
    POST /inventory/reorder-scan compares stock against effective thresholds, checks remaining capacity, selects the preferred/default supplier, and creates purchase orders (or simulates them with dry_run). Receiving a purchase order (PUT /purchase-orders/{id}/status) restocks the warehouse and recalculates aggregate product stock.

  5. Customer Orders
    Authenticated users place orders; inventory is reserved from the selected warehouse and replenished if an order is cancelled. Admins can adjust order status.

  6. Operational Guardrails
    Per-client rate limiting and CORS configuration protect the API, while /healthz verifies database connectivity for readiness probes.


Usage Guide

  1. Run the backend

    export JWT_SECRET=local-dev-secret
    go run main.go
    
  2. Seed core data
    Use docs/sample_data.json as a reference. Create suppliers, warehouses, and products (including reorder_threshold) via the provided APIs, then upsert warehouse inventory. When warehouse-level thresholds are omitted or set to 0, the product-level threshold drives automatic reorders. sample_data.json should be used to drive a handful of API calls in the order shown so you end up with a fully functional dataset:

    • Suppliers – POST each supplier to /api/v1/suppliers with the JSON body from the suppliers array.
    • Warehouses – POST each warehouse to /api/v1/warehouses.
    • Products – POST to /api/v1/products, including the default_supplier_id field; after you create suppliers you can copy their IDs into these payloads.
    • Warehouse Inventory – POST to /api/v1/inventory for each entry: supply the warehouse ID, product ID, quantity, and optional preferred_supplier_id.
  3. Trigger reordering
    Call POST /api/v1/inventory/reorder-scan (optionally with dry_run=true) to generate purchase orders whenever stock dips below the effective thresholds while respecting warehouse capacity.

  4. Acknowledge replenishment
    When stock arrives, use PUT /api/v1/purchase-orders/{id}/status with {"status":"Received"} to add quantities back into the warehouse and recalculate product availability.

  5. Adjust stock manually
    Use POST /api/v1/inventory/{warehouseId}/{productId}/adjust with a positive or negative quantity_delta to simulate sales, shrinkage, or corrections.


License

This project is licensed under the MIT License.


Notes
  • Ensure proper error handling for invalid inputs and missing parameters.
  • All requests requiring authentication must include the Authorization header with the format:
    {
      "Authorization": "Bearer <JWT_TOKEN>"
    }
    

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
Package docs Code generated by swaggo/swag.
Package docs Code generated by swaggo/swag.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL