JD Book Uploader - Backend API Documentation

Overview

Backend API for uploading book and stationery products with images to Firebase Storage and storing metadata in PostgreSQL.

Base URL

  • Development: http://localhost:8080
  • Production: Configure via FRONTEND_URL environment variable

API Endpoints

1. Health Check

Endpoint: GET /api/health

Description: Check the health status of the API and its dependencies (database and Firebase).

Request:

  • Method: GET
  • Path: /api/health
  • Headers: None required
  • Body: None

Response:

Success (200 OK):

{
  "status": "ok",
  "database": "connected",
  "firebase": "connected"
}

Service Unavailable (503):

{
  "status": "degraded",
  "database": "disconnected",
  "firebase": "connected"
}

Response Fields:

  • status (string): "ok" if all services connected, "degraded" if any service unavailable
  • database (string): "connected", "disconnected", or "not_initialized"
  • firebase (string): "connected", "disconnected", or "not_initialized"

2. Upload Book

Endpoint: POST /api/books

Description: Upload a book product with cover image. Image is uploaded to Firebase Storage and book metadata is stored in PostgreSQL.

Request:

  • Method: POST
  • Path: /api/books
  • Content-Type: multipart/form-data
  • Body: FormData with the following fields:

Required Parameters:

  • image (File) - Book cover image file

    • Type: File (multipart/form-data)
    • Allowed types: image/png, image/jpeg, image/jpg, image/webp
    • Max size: 10MB
    • Required: Yes
  • book_name (string) - Book name

    • Type: String
    • Max length: 200 characters
    • Required: Yes
    • Validation: Not empty
  • cost (number) - Cost price

    • Type: Number (decimal)
    • Required: Yes
    • Validation: Must be a positive number
  • price (number) - Selling price

    • Type: Number (decimal)
    • Required: Yes
    • Validation: Must be a positive number
  • quantity (integer) - Stock quantity

    • Type: Integer
    • Required: Yes
    • Validation: Must be a positive integer
  • publisher_author (string) - Publisher or author name

    • Type: String
    • Max length: 200 characters
    • Required: Yes
    • Validation: Not empty
  • category (string) - Book category

    • Type: String
    • Max length: 100 characters
    • Required: Yes
    • Validation: Not empty

Optional Parameters:

  • discount (number) - Discount amount

    • Type: Number (decimal)
    • Default: 0
    • Required: No
    • Validation: Must be non-negative (>= 0)
  • description (string) - Book description

    • Type: String
    • Max length: 1000 characters
    • Required: No

Request Body Format:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary...

------WebKitFormBoundary...
Content-Disposition: form-data; name="image"; filename="book-cover.png"
Content-Type: image/png

[binary image data]
------WebKitFormBoundary...
Content-Disposition: form-data; name="book_name"

The Great Gatsby
------WebKitFormBoundary...
Content-Disposition: form-data; name="cost"

15.50
------WebKitFormBoundary...
Content-Disposition: form-data; name="price"

24.99
------WebKitFormBoundary...
Content-Disposition: form-data; name="discount"

0
------WebKitFormBoundary...
Content-Disposition: form-data; name="quantity"

100
------WebKitFormBoundary...
Content-Disposition: form-data; name="publisher_author"

F. Scott Fitzgerald
------WebKitFormBoundary...
Content-Disposition: form-data; name="category"

Fiction
------WebKitFormBoundary...
Content-Disposition: form-data; name="description"

A classic American novel
------WebKitFormBoundary...--

Success Response (201 Created):

{
  "success": true,
  "data": {
    "book_code": "550e8400-e29b-41d4-a716-446655440000",
    "book_name": "The Great Gatsby",
    "image_url": "https://storage.googleapis.com/download/storage/v1/b/kaisa-341a6.appspot.com/o/images%2F2025%2F11%2F1732123456_abc123.png?generation=1732123456789&alt=media",
    "slug": "the-great-gatsby",
    "created_at": "2025-11-20T17:30:00Z"
  }
}

Response Fields:

  • success (boolean): Always true on success
  • data (object): Contains the created book data
    • book_code (string): UUID generated for the book
    • book_name (string): Book name as provided
    • image_url (string): Public URL of uploaded image in Firebase Storage
    • slug (string): URL-friendly slug generated from book name
    • created_at (string): ISO 8601 timestamp of creation

Error Responses:

400 Bad Request - Validation error:

{
  "success": false,
  "error": "book_name is required"
}

413 Payload Too Large - File size exceeds limit:

{
  "success": false,
  "error": "File size exceeds 10MB limit"
}

500 Internal Server Error - Server error:

{
  "success": false,
  "error": "Failed to upload image"
}

Common Error Messages:

  • "Image file is required" - No image file provided
  • "Invalid file type. Only PNG, JPEG, and WEBP are allowed" - Unsupported file type
  • "File size exceeds 10MB limit" - File too large
  • "book_name is required" - Missing required field
  • "Invalid cost value" - Invalid number format or negative value
  • "Failed to parse form data" - Malformed request
  • "Failed to upload image" - Firebase upload error
  • "Failed to create book" - Database insert error

3. Upload Stationery

Endpoint: POST /api/stationery

Description: Upload a stationery product with image. Image is uploaded to Firebase Storage and stationery metadata is stored in PostgreSQL.

Request:

  • Method: POST
  • Path: /api/stationery
  • Content-Type: multipart/form-data
  • Body: FormData with the following fields:

Required Parameters:

  • image (File) - Stationery image file

    • Type: File (multipart/form-data)
    • Allowed types: image/png, image/jpeg, image/jpg, image/webp
    • Max size: 10MB
    • Required: Yes
  • stationery_name (string) - Stationery name

    • Type: String
    • Max length: 200 characters
    • Required: Yes
    • Validation: Not empty
  • cost (number) - Cost price

    • Type: Number (decimal)
    • Required: Yes
    • Validation: Must be a positive number
  • price (number) - Selling price

    • Type: Number (decimal)
    • Required: Yes
    • Validation: Must be a positive number
  • quantity (integer) - Stock quantity

    • Type: Integer
    • Required: Yes
    • Validation: Must be a positive integer
  • category (string) - Stationery category

    • Type: String
    • Max length: 100 characters
    • Required: Yes
    • Validation: Not empty

Optional Parameters:

  • discount (number) - Discount amount

    • Type: Number (decimal)
    • Default: 0
    • Required: No
    • Validation: Must be non-negative (>= 0)
  • color (string) - Product color

    • Type: String
    • Required: No
  • material (string) - Product material

    • Type: String
    • Required: No
  • dimensions (string) - Product dimensions

    • Type: String
    • Required: No
  • description (string) - Product description

    • Type: String
    • Max length: 1000 characters
    • Required: No

Request Body Format:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundary...

------WebKitFormBoundary...
Content-Disposition: form-data; name="image"; filename="pen.png"
Content-Type: image/png

[binary image data]
------WebKitFormBoundary...
Content-Disposition: form-data; name="stationery_name"

Blue Ballpoint Pen
------WebKitFormBoundary...
Content-Disposition: form-data; name="cost"

2.50
------WebKitFormBoundary...
Content-Disposition: form-data; name="price"

5.99
------WebKitFormBoundary...
Content-Disposition: form-data; name="discount"

0
------WebKitFormBoundary...
Content-Disposition: form-data; name="quantity"

200
------WebKitFormBoundary...
Content-Disposition: form-data; name="category"

Writing
------WebKitFormBoundary...
Content-Disposition: form-data; name="color"

Blue
------WebKitFormBoundary...
Content-Disposition: form-data; name="material"

Plastic
------WebKitFormBoundary...
Content-Disposition: form-data; name="dimensions"

15cm
------WebKitFormBoundary...
Content-Disposition: form-data; name="description"

Smooth writing ballpoint pen
------WebKitFormBoundary...--

Success Response (201 Created):

{
  "success": true,
  "data": {
    "stationery_code": "550e8400-e29b-41d4-a716-446655440001",
    "stationery_name": "Blue Ballpoint Pen",
    "image_url": "https://storage.googleapis.com/download/storage/v1/b/kaisa-341a6.appspot.com/o/images%2F2025%2F11%2F1732123456_def456.png?generation=1732123456789&alt=media",
    "slug": "blue-ballpoint-pen",
    "created_at": "2025-11-20T17:30:00Z"
  }
}

Response Fields:

  • success (boolean): Always true on success
  • data (object): Contains the created stationery data
    • stationery_code (string): UUID generated for the stationery item
    • stationery_name (string): Stationery name as provided
    • image_url (string): Public URL of uploaded image in Firebase Storage
    • slug (string): URL-friendly slug generated from stationery name
    • created_at (string): ISO 8601 timestamp of creation

Error Responses:

400 Bad Request - Validation error:

{
  "success": false,
  "error": "stationery_name is required"
}

413 Payload Too Large - File size exceeds limit:

{
  "success": false,
  "error": "File size exceeds 10MB limit"
}

500 Internal Server Error - Server error:

{
  "success": false,
  "error": "Failed to upload image"
}

Common Error Messages:

  • "Image file is required" - No image file provided
  • "Invalid file type. Only PNG, JPEG, and WEBP are allowed" - Unsupported file type
  • "File size exceeds 10MB limit" - File too large
  • "stationery_name is required" - Missing required field
  • "Invalid cost value" - Invalid number format or negative value
  • "Failed to parse form data" - Malformed request
  • "Failed to upload image" - Firebase upload error
  • "Failed to create stationery" - Database insert error

Response Format

All API responses follow a consistent structure:

Success Response:

{
  "success": true,
  "data": { ... }
}

Error Response:

{
  "success": false,
  "error": "Error message"
}

HTTP Status Codes

  • 200 OK - Health check successful
  • 201 Created - Resource created successfully (book/stationery uploaded)
  • 400 Bad Request - Validation error or invalid input
  • 413 Payload Too Large - File size exceeds 10MB limit
  • 500 Internal Server Error - Server error (database, Firebase, or internal error)
  • 503 Service Unavailable - Service dependencies (database or Firebase) unavailable

Field Validation Summary

Book Upload Fields

Field Type Required Max Length Validation Rules
image File Yes 10MB PNG, JPEG, or WEBP only
book_name string Yes 200 Not empty
cost number Yes - Positive number (> 0)
price number Yes - Positive number (> 0)
discount number No - Non-negative (>= 0), default: 0
quantity integer Yes - Positive integer (> 0)
publisher_author string Yes 200 Not empty
category string Yes 100 Not empty
description string No 1000 -

Stationery Upload Fields

Field Type Required Max Length Validation Rules
image File Yes 10MB PNG, JPEG, or WEBP only
stationery_name string Yes 200 Not empty
cost number Yes - Positive number (> 0)
price number Yes - Positive number (> 0)
discount number No - Non-negative (>= 0), default: 0
quantity integer Yes - Positive integer (> 0)
category string Yes 100 Not empty
color string No - -
material string No - -
dimensions string No - -
description string No 1000 -

Notes

  1. Content-Type Header: For file uploads, use multipart/form-data. Do NOT manually set the Content-Type header - let the browser/client set it automatically with the proper boundary.

  2. Image Processing: The backend expects images to be processed on the frontend (resized to 1000x1000, background removed) before upload. Upload the processed PNG image.

  3. Auto-Generated Fields: The following fields are automatically generated by the backend:

    • book_code / stationery_code: UUID v4
    • slug: Generated from product name (lowercase, spaces to hyphens, special characters removed)
    • created_at: Current timestamp
    • updated_at: Current timestamp
  4. File Upload: Only one image file per request. The file field name must be image.

  5. Error Messages: Always check the error field in error responses for user-friendly error messages.

  6. CORS: The backend is configured to accept requests from the frontend URL specified in FRONTEND_URL environment variable (default: http://localhost:5173).

Description
No description provided
Readme 120 KiB
Languages
Go 96.8%
Shell 3.2%