Skip to main content

E-Commerce Search Engine

Author: Josh S. Sakweli, Backend Lead Team Last Updated: 2025-12-30 Version: v1.0

Base URL: https://api.nextgate.com/api/v1

Short Description: Public search endpoints for discovering shops, products, and active purchase groups on the Nexgate e-commerce platform. These endpoints power the main search functionality, product discovery, and group buying features across the application.

Hints:

  • Search query must be at least 2 characters
  • Results are ordered alphabetically by name
  • Filter by type using the type parameter: SHOP, PRODUCT, or PURCHASE_GROUP
  • Purchase groups are searched by both name and group code (e.g., GP-ABC123)
  • All endpoints are public and do not require authentication
  • Maximum page size is 50 results
  • Rate limiting applies to prevent abuse

Standard Response Format

All API responses follow a consistent structure using our Globe Response Builder pattern:

Success Response Structure

{
  "success": true,
  "httpStatus": "OK",
  "message": "Operation completed successfully",
  "action_time": "2025-12-30T10:30:45",
  "data": {
    // Actual response data goes here
  }
}

Error Response Structure

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Error description",
  "action_time": "2025-12-30T10:30:45",
  "data": null
}

Search Result Types

Each search result contains a type field indicating the result category:

Type Description Specific Fields
SHOP Active shop/store shop object
PRODUCT Active product product object
PURCHASE_GROUP Open group purchase purchaseGroup object

Endpoints


Purpose: Search across all types (shops, products, and purchase groups) with optional type filtering

Endpoint: GET {base_url}/api/v1/e-commerce/search

Access Level: 🌐 Public (No Authentication Required)

Query Parameters:

Parameter Type Required Default Description Validation
query string Yes - Search term Min 2 characters
type string No null (all) Filter by type SHOP, PRODUCT, PURCHASE_GROUP
page int No 1 Page number Min 1
size int No 20 Results per page Max 50

Request Examples:

# Search all types
GET /api/v1/e-commerce/search?query=iphone&page=1&size=20

# Search shops only
GET /api/v1/e-commerce/search?query=fashion&type=SHOP

# Search products only
GET /api/v1/e-commerce/search?query=dress&type=PRODUCT

# Search purchase groups only
GET /api/v1/e-commerce/search?query=GP-ABC123&type=PURCHASE_GROUP

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Search results retrieved successfully",
  "action_time": "2025-12-30T10:30:45",
  "data": {
    "content": [
      {
        "type": "SHOP",
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Fashion Hub TZ",
        "imageUrl": "https://storage.example.com/shops/logo1.jpg",
        "shop": {
          "slug": "fashion-hub-tz",
          "isVerified": true,
          "rating": 4.8,
          "productsCount": 156,
          "location": "Dar es Salaam"
        }
      },
      {
        "type": "PRODUCT",
        "id": "660e8400-e29b-41d4-a716-446655440001",
        "name": "Summer Fashion Dress",
        "imageUrl": "https://storage.example.com/products/dress1.jpg",
        "product": {
          "slug": "summer-fashion-dress",
          "price": 45000.00,
          "discountPrice": 38000.00,
          "currency": "TZS",
          "inStock": true,
          "rating": 4.5,
          "shopName": "Fashion Hub TZ",
          "shopIsVerified": true
        }
      },
      {
        "type": "PURCHASE_GROUP",
        "id": "770e8400-e29b-41d4-a716-446655440002",
        "name": "Fashion Week Special Deal",
        "imageUrl": "https://storage.example.com/products/bundle.jpg",
        "purchaseGroup": {
          "groupCode": "GP-FWS123",
          "regularPrice": 150000.00,
          "groupPrice": 120000.00,
          "savingsPercentage": 20,
          "seatsRemaining": 3,
          "totalSeats": 10,
          "expiresAt": "2025-12-31T23:59:59",
          "shopName": "Fashion Hub TZ"
        }
      }
    ],
    "page": 0,
    "size": 20,
    "totalElements": 3,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "shopsCount": 1,
    "productsCount": 1,
    "purchaseGroupsCount": 1
  }
}

Response Fields:

Field Type Description
content array List of search result items
page int Current page number (0-indexed)
size int Page size
totalElements long Total number of results
totalPages int Total number of pages
hasNext boolean Whether more pages exist
hasPrevious boolean Whether previous pages exist
shopsCount long Total shops matching query
productsCount long Total products matching query
purchaseGroupsCount long Total purchase groups matching query

Error Response JSON Samples:

Query too short:

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Search query must be at least 2 characters",
  "action_time": "2025-12-30T10:30:45",
  "data": null
}

Invalid type:

{
  "success": false,
  "httpStatus": "BAD_REQUEST",
  "message": "Invalid type. Must be: SHOP, PRODUCT, or PURCHASE_GROUP",
  "action_time": "2025-12-30T10:30:45",
  "data": null
}

2. Search Shops

Purpose: Search for active shops by name

Endpoint: GET {base_url}/api/v1/e-commerce/search/shops

Access Level: 🌐 Public (No Authentication Required)

Query Parameters:

Parameter Type Required Default Description Validation
query string Yes - Shop name search term Min 2 characters
page int No 1 Page number Min 1
size int No 20 Results per page Max 50

Request Example:

GET /api/v1/e-commerce/search/shops?query=fashion&page=1&size=20

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Shops retrieved successfully",
  "action_time": "2025-12-30T10:30:45",
  "data": {
    "content": [
      {
        "type": "SHOP",
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Fashion Hub TZ",
        "imageUrl": "https://storage.example.com/shops/logo1.jpg",
        "shop": {
          "slug": "fashion-hub-tz",
          "isVerified": true,
          "rating": 4.8,
          "productsCount": 156,
          "location": "Dar es Salaam"
        }
      },
      {
        "type": "SHOP",
        "id": "660e8400-e29b-41d4-a716-446655440001",
        "name": "Fashion Zone",
        "imageUrl": "https://storage.example.com/shops/logo2.jpg",
        "shop": {
          "slug": "fashion-zone",
          "isVerified": false,
          "rating": 4.2,
          "productsCount": 89,
          "location": "Arusha"
        }
      }
    ],
    "page": 0,
    "size": 20,
    "totalElements": 2,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "shopsCount": 2,
    "productsCount": 0,
    "purchaseGroupsCount": 0
  }
}

Shop Info Fields:

Field Type Description
slug string URL-friendly shop identifier
isVerified boolean Whether shop is verified
rating double Shop rating (0-5)
productsCount int Number of active products
location string Shop location

3. Search Products

Purpose: Search for active products by name

Endpoint: GET {base_url}/api/v1/e-commerce/search/products

Access Level: 🌐 Public (No Authentication Required)

Query Parameters:

Parameter Type Required Default Description Validation
query string Yes - Product name search term Min 2 characters
page int No 1 Page number Min 1
size int No 20 Results per page Max 50

Request Example:

GET /api/v1/e-commerce/search/products?query=dress&page=1&size=20

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Products retrieved successfully",
  "action_time": "2025-12-30T10:30:45",
  "data": {
    "content": [
      {
        "type": "PRODUCT",
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "Summer Dress Collection",
        "imageUrl": "https://storage.example.com/products/dress1.jpg",
        "product": {
          "slug": "summer-dress-collection",
          "price": 45000.00,
          "discountPrice": 38000.00,
          "currency": "TZS",
          "inStock": true,
          "rating": 4.5,
          "shopName": "Fashion Hub TZ",
          "shopIsVerified": true
        }
      },
      {
        "type": "PRODUCT",
        "id": "660e8400-e29b-41d4-a716-446655440001",
        "name": "Evening Dress",
        "imageUrl": "https://storage.example.com/products/dress2.jpg",
        "product": {
          "slug": "evening-dress",
          "price": 85000.00,
          "discountPrice": null,
          "currency": "TZS",
          "inStock": true,
          "rating": 4.8,
          "shopName": "Elegance Store",
          "shopIsVerified": true
        }
      },
      {
        "type": "PRODUCT",
        "id": "770e8400-e29b-41d4-a716-446655440002",
        "name": "Casual Dress",
        "imageUrl": "https://storage.example.com/products/dress3.jpg",
        "product": {
          "slug": "casual-dress",
          "price": 35000.00,
          "discountPrice": 30000.00,
          "currency": "TZS",
          "inStock": false,
          "rating": 4.0,
          "shopName": "Budget Fashion",
          "shopIsVerified": false
        }
      }
    ],
    "page": 0,
    "size": 20,
    "totalElements": 3,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "shopsCount": 0,
    "productsCount": 3,
    "purchaseGroupsCount": 0
  }
}

Product Info Fields:

Field Type Description
slug string URL-friendly product identifier
price decimal Regular price
discountPrice decimal Discounted price (null if no discount)
currency string Price currency (e.g., TZS)
inStock boolean Whether product is in stock
rating double Product rating (0-5)
shopName string Name of the shop selling this product
shopIsVerified boolean Whether the shop is verified

4. Search Purchase Groups

Purpose: Search for active (open, non-expired) purchase groups by name or group code

Endpoint: GET {base_url}/api/v1/e-commerce/search/groups

Access Level: 🌐 Public (No Authentication Required)

Query Parameters:

Parameter Type Required Default Description Validation
query string Yes - Group name or code Min 2 characters
page int No 1 Page number Min 1
size int No 20 Results per page Max 50

Request Examples:

# Search by group name
GET /api/v1/e-commerce/search/groups?query=iPhone Deal&page=1&size=20

# Search by group code
GET /api/v1/e-commerce/search/groups?query=GP-ABC123&page=1&size=20

Success Response JSON Sample:

{
  "success": true,
  "httpStatus": "OK",
  "message": "Purchase groups retrieved successfully",
  "action_time": "2025-12-30T10:30:45",
  "data": {
    "content": [
      {
        "type": "PURCHASE_GROUP",
        "id": "550e8400-e29b-41d4-a716-446655440000",
        "name": "iPhone 15 Pro - Dar es Salaam Deal",
        "imageUrl": "https://storage.example.com/products/iphone15.jpg",
        "purchaseGroup": {
          "groupCode": "GP-ABC123",
          "regularPrice": 2500000.00,
          "groupPrice": 2200000.00,
          "savingsPercentage": 12,
          "seatsRemaining": 2,
          "totalSeats": 5,
          "expiresAt": "2025-12-31T08:00:00",
          "shopName": "TechZone TZ"
        }
      },
      {
        "type": "PURCHASE_GROUP",
        "id": "660e8400-e29b-41d4-a716-446655440001",
        "name": "iPhone 15 Plus Group Buy",
        "imageUrl": "https://storage.example.com/products/iphone15plus.jpg",
        "purchaseGroup": {
          "groupCode": "GP-XYZ789",
          "regularPrice": 2200000.00,
          "groupPrice": 1950000.00,
          "savingsPercentage": 11,
          "seatsRemaining": 4,
          "totalSeats": 8,
          "expiresAt": "2025-12-30T18:00:00",
          "shopName": "Mobile World"
        }
      }
    ],
    "page": 0,
    "size": 20,
    "totalElements": 2,
    "totalPages": 1,
    "hasNext": false,
    "hasPrevious": false,
    "shopsCount": 0,
    "productsCount": 0,
    "purchaseGroupsCount": 2
  }
}

Purchase Group Info Fields:

Field Type Description
groupCode string Unique group code (e.g., GP-ABC123)
regularPrice decimal Original product price
groupPrice decimal Discounted group price
savingsPercentage int Percentage saved
seatsRemaining int Available seats
totalSeats int Total seats in group
expiresAt datetime Group expiration time
shopName string Name of the shop

Note: Only returns groups that are:

  • Status: OPEN
  • Not expired (expiresAt > now)
  • Not deleted

Endpoints Summary

# Method Endpoint Purpose
1 GET /api/v1/e-commerce/search Search all types
2 GET /api/v1/e-commerce/search/shops Search shops only
3 GET /api/v1/e-commerce/search/products Search products only
4 GET /api/v1/e-commerce/search/groups Search purchase groups only

Common Error Responses

Status Message Cause
400 BAD_REQUEST Search query must be at least 2 characters Query too short
400 BAD_REQUEST Invalid type. Must be: SHOP, PRODUCT, or PURCHASE_GROUP Invalid type parameter
429 TOO_MANY_REQUESTS Rate limit exceeded Too many requests

Frontend Usage Examples

JavaScript/TypeScript

// Global search
const searchAll = async (query, page = 1, size = 20) => {
  const response = await fetch(
    `/api/v1/e-commerce/search?query=${encodeURIComponent(query)}&page=${page}&size=${size}`
  );
  return response.json();
};

// Search with type filter
const searchByType = async (query, type, page = 1) => {
  const response = await fetch(
    `/api/v1/e-commerce/search?query=${encodeURIComponent(query)}&type=${type}&page=${page}`
  );
  return response.json();
};

// Search shops only
const searchShops = async (query) => {
  const response = await fetch(
    `/api/v1/e-commerce/search/shops?query=${encodeURIComponent(query)}`
  );
  return response.json();
};

// Search products only
const searchProducts = async (query) => {
  const response = await fetch(
    `/api/v1/e-commerce/search/products?query=${encodeURIComponent(query)}`
  );
  return response.json();
};

// Search groups by name or code
const searchGroups = async (query) => {
  const response = await fetch(
    `/api/v1/e-commerce/search/groups?query=${encodeURIComponent(query)}`
  );
  return response.json();
};

Rendering Results

const renderResults = (results) => {
  results.content.forEach(item => {
    switch (item.type) {
      case 'SHOP':
        renderShopCard(item);
        break;
      case 'PRODUCT':
        renderProductCard(item);
        break;
      case 'PURCHASE_GROUP':
        renderGroupCard(item);
        break;
    }
  });
  
  // Show counts
  console.log(`Found: ${results.shopsCount} shops, ${results.productsCount} products, ${results.purchaseGroupsCount} groups`);
};