# Unified Webshop API Response Format

**Related:** Issue #53 - Unified webshop API response format

## Overview

All Shop Data API endpoints now return responses in a **consistent, unified format** regardless of the e-commerce platform (Shopify, WooCommerce, or ShopRenter). This standardization makes it easier to build platform-agnostic applications and handle responses uniformly.

## Response Structure

### Base Response Interface

```typescript
interface UnifiedApiResponse<T> {
  success: boolean;              // Request success status
  data: T | T[];                 // Response data (single item or array)
  metadata: ResponseMetadata;    // Request metadata
  pagination?: Pagination;       // Only for list endpoints
  error?: ErrorDetails;          // Only when success=false
}
```

### Metadata

Every response includes comprehensive metadata:

```typescript
interface ResponseMetadata {
  platform?: string;           // "shopify" | "woocommerce" | "shoprenter"
  store_id?: string;          // Store UUID (for data endpoints)
  resource_type: string;      // "customers" | "orders" | "products" | "stores"
  auth_type: "user" | "internal";  // Authentication type used
  fetched_at: string;         // ISO 8601 timestamp
  request_id?: string;        // Unique request tracking ID
}
```

### Pagination (List Endpoints)

Paginated responses include:

```typescript
interface Pagination {
  page: number;              // Current page (1-indexed)
  limit: number;             // Items per page
  total?: number;            // Total items (if available)
  has_more: boolean;         // Whether more pages exist
  next_page: number | null;  // Next page number or null
  prev_page: number | null;  // Previous page number or null
}
```

### Error Response

Failed requests return:

```typescript
interface ErrorDetails {
  code: string;        // Error code for programmatic handling
  message: string;     // Human-readable error message
  details?: unknown;   // Additional error context
}
```

## Response Examples

### List Response (Paginated)

**Request:**
```bash
GET /shop-data-api/products?store_id={uuid}&page=1&limit=25
Authorization: Bearer int_shopcall_xxx
```

**Response:**
```json
{
  "success": true,
  "data": [
    {
      "id": "7654321",
      "platform": "shopify",
      "title": "Awesome Product",
      "price": 29.99,
      "currency": "USD",
      "status": "active",
      ...
    },
    {
      "id": "7654322",
      "platform": "shopify",
      "title": "Another Product",
      "price": 49.99,
      "currency": "USD",
      "status": "active",
      ...
    }
  ],
  "metadata": {
    "platform": "shopify",
    "store_id": "73ca58c0-e47f-4caa-bcdb-2d0b1fda27ce",
    "resource_type": "products",
    "auth_type": "internal",
    "fetched_at": "2025-11-01T13:45:30.123Z",
    "request_id": "req_1730462730_a8f3c9d2"
  },
  "pagination": {
    "page": 1,
    "limit": 25,
    "has_more": true,
    "next_page": 2,
    "prev_page": null
  }
}
```

### Single Item Response

**Request:**
```bash
GET /shop-data-api/orders/12345?store_id={uuid}
Authorization: Bearer api_shopcall_xxx
```

**Response:**
```json
{
  "success": true,
  "data": {
    "id": "12345",
    "platform": "woocommerce",
    "order_number": "1001",
    "status": "completed",
    "total_price": 129.99,
    "currency": "USD",
    "customer_name": "John Doe",
    "customer_email": "john@example.com",
    ...
  },
  "metadata": {
    "platform": "woocommerce",
    "store_id": "73ca58c0-e47f-4caa-bcdb-2d0b1fda27ce",
    "resource_type": "orders",
    "auth_type": "user",
    "fetched_at": "2025-11-01T13:45:30.123Z",
    "request_id": "req_1730462730_b9e4d1c3"
  }
}
```

### Error Response

**Request:**
```bash
GET /shop-data-api/customers?store_id=invalid-uuid
Authorization: Bearer int_shopcall_xxx
```

**Response (404):**
```json
{
  "success": false,
  "data": [],
  "metadata": {
    "resource_type": "customers",
    "auth_type": "internal",
    "fetched_at": "2025-11-01T13:45:30.123Z"
  },
  "error": {
    "code": "STORE_NOT_FOUND",
    "message": "Store not found or access denied"
  }
}
```

### Stores List Response

**Request:**
```bash
GET /shop-data-api/stores
Authorization: Bearer int_shopcall_xxx
```

**Response:**
```json
{
  "success": true,
  "data": [
    {
      "id": "uuid-1",
      "store_name": "My Shopify Store",
      "store_url": "mystore.myshopify.com",
      "platform_name": "shopify",
      "is_active": true,
      "connected_at": "2025-10-01T10:00:00Z",
      "sync_status": "completed"
    },
    {
      "id": "uuid-2",
      "store_name": "WooCommerce Shop",
      "store_url": "woo.example.com",
      "platform_name": "woocommerce",
      "is_active": true,
      "connected_at": "2025-10-15T14:30:00Z",
      "sync_status": "idle"
    }
  ],
  "metadata": {
    "resource_type": "stores",
    "auth_type": "internal",
    "fetched_at": "2025-11-01T13:45:30.123Z",
    "request_id": "req_1730462730_c7f5e2d4"
  },
  "pagination": {
    "page": 1,
    "limit": 2,
    "total": 2,
    "has_more": false,
    "next_page": null,
    "prev_page": null
  }
}
```

## Error Codes

Common error codes returned by the API:

| Code | HTTP Status | Description |
|------|-------------|-------------|
| `METHOD_NOT_ALLOWED` | 405 | Only GET requests are supported |
| `INVALID_ENDPOINT` | 404 | Invalid API endpoint |
| `MISSING_STORE_ID` | 400 | Required parameter `store_id` not provided |
| `INVALID_RESOURCE` | 400 | Invalid resource type (must be customers, orders, products, or stores) |
| `INVALID_PAGINATION` | 400 | Invalid pagination parameters |
| `STORE_NOT_FOUND` | 404 | Store not found or access denied |
| `ACCESS_DENIED` | 403 | User does not have permission to access this resource |
| `UNSUPPORTED_PLATFORM` | 400 | Platform is not supported |
| `FETCH_ERROR` | 500 | Error fetching data from e-commerce platform |
| `MISSING_API_KEY` | 401 | No API key provided in Authorization header |

## Benefits of Unified Format

### 1. **Platform-Agnostic Code**

You can write the same code to handle responses from any platform:

```typescript
async function fetchProducts(storeId: string) {
  const response = await fetch(
    `/shop-data-api/products?store_id=${storeId}`,
    { headers: { Authorization: `Bearer ${apiKey}` } }
  );

  const result = await response.json();

  // Same handling for Shopify, WooCommerce, ShopRenter
  if (result.success) {
    return result.data.map(product => ({
      id: product.id,
      name: product.title,
      price: product.price,
      platform: result.metadata.platform  // Track which platform
    }));
  } else {
    throw new Error(result.error.message);
  }
}
```

### 2. **Consistent Error Handling**

```typescript
function handleApiError(response: UnifiedApiResponse<any>) {
  if (!response.success && response.error) {
    switch (response.error.code) {
      case 'STORE_NOT_FOUND':
        alert('Store not found');
        break;
      case 'ACCESS_DENIED':
        alert('You do not have permission to access this resource');
        break;
      default:
        alert(`Error: ${response.error.message}`);
    }
  }
}
```

### 3. **Uniform Pagination**

```typescript
function renderPagination(pagination: Pagination) {
  return (
    <div>
      {pagination.prev_page && (
        <button onClick={() => loadPage(pagination.prev_page)}>
          Previous
        </button>
      )}
      <span>Page {pagination.page}</span>
      {pagination.next_page && (
        <button onClick={() => loadPage(pagination.next_page)}>
          Next
        </button>
      )}
    </div>
  );
}
```

### 4. **Request Tracking**

```typescript
// Track requests for debugging
console.log(`Request ID: ${response.metadata.request_id}`);
console.log(`Fetched at: ${response.metadata.fetched_at}`);
console.log(`Platform: ${response.metadata.platform}`);
console.log(`Auth type: ${response.metadata.auth_type}`);
```

## Available Endpoints

All endpoints use the unified response format:

- `GET /shop-data-api/stores` - List all stores
- `GET /shop-data-api/products?store_id={uuid}` - List products
- `GET /shop-data-api/products/{id}?store_id={uuid}` - Get single product
- `GET /shop-data-api/orders?store_id={uuid}` - List orders
- `GET /shop-data-api/orders/{id}?store_id={uuid}` - Get single order
- `GET /shop-data-api/customers?store_id={uuid}` - List customers
- `GET /shop-data-api/customers/{id}?store_id={uuid}` - Get single customer

### Query Parameters

- `store_id` (required for data endpoints) - Store UUID
- `page` (optional, default: 1) - Page number (1-indexed)
- `limit` (optional, default: 25, max: 100) - Items per page
- `status` (optional, orders only) - Filter by order status

## Implementation Details

### Files

- **Response types:** `supabase/functions/_shared/unified-response.ts`
- **API implementation:** `supabase/functions/shop-data-api/index.ts`
- **Platform adapters:** `supabase/functions/_shared/platform-adapters.ts`

### Helper Functions

```typescript
// Create success response for single item
createSuccessResponse(data, metadata)

// Create list response with pagination
createListResponse(data, metadata, pagination)

// Create error response
createErrorResponse(code, message, details?, status?)

// Convert to HTTP Response
toHttpResponse(response, status?)

// Calculate has_more flag
calculateHasMore(returnedCount, requestedLimit)

// Generate unique request ID
generateRequestId()
```

## Migration Guide

If you have existing code that relies on the old response format:

### Before (Old Format)

```typescript
const response = await fetch('/shop-data-api/products');
const data = await response.json();

// Different structure per platform
if (data.success) {
  const products = data.data;
  const page = data.pagination?.page;
  const platform = data.platform;  // May or may not exist
}
```

### After (Unified Format)

```typescript
const response = await fetch('/shop-data-api/products?store_id=uuid');
const data = await response.json();

// Same structure for all platforms
if (data.success) {
  const products = data.data;
  const page = data.pagination.page;
  const platform = data.metadata.platform;  // Always exists
  const requestId = data.metadata.request_id;  // New: track requests
}
```

## See Also

- [Internal API Keys Documentation](INTERNAL_API_KEYS.md)
- [Platform Adapters](supabase/functions/_shared/platform-adapters.ts)
- Issue #50 - Non user-based API keys
- Issue #53 - Unified webshop API
