Skip to main content
Version: 0.14.0 (Previous)

📦 Product Service

Testing Status

The Product Service provides a complete REST API for managing product entities with comprehensive CRUD operations and powerful batch processing capabilities. It's built using the Nodeblocks functional composition approach and integrates seamlessly with MongoDB.


🚀 Quickstart​

import express from 'express';
import {middlewares, services, drivers} from '@nodeblocks/backend-sdk';

const {nodeBlocksErrorMiddleware} = middlewares;
const {productService} = services;
const {withMongo, createFileStorageDriver} = drivers;

const connectToDatabase = withMongo('mongodb://localhost:27017/?authSource=admin', 'dev', 'user', 'password');

// Required for image upload URL endpoint:
// Ensure GOOGLE_APPLICATION_CREDENTIALS is set to your GCP service account JSON.
const fileStorageDriver = createFileStorageDriver('your-gcp-project-id', 'your-bucket-name');

express()
.use(
productService(
{
...(await connectToDatabase('products')),
...(await connectToDatabase('identities')),
...(await connectToDatabase('profiles')), // Required for likers feature
...(await connectToDatabase('productVariants')), // Optional: product variants
...(await connectToDatabase('organizations')), // Required for org-scoped product lists
},
{
authSecrets: {
authEncSecret: 'your-encryption-secret',
authSignSecret: 'your-signing-secret',
},
authMode: 'bearer', // or 'cookie'
identity: {
typeIds: {
admin: '100',
guest: '000',
regular: '001',
},
},
organization: {
roles: {
admin: 'admin',
member: 'member',
owner: 'owner',
},
},
},
{fileStorageDriver},
),
)
.use(nodeBlocksErrorMiddleware())
.listen(8089, () => console.log('Server running'));

📋 Endpoint Summary​

Individual Product Operations​

MethodPathDescriptionAuthorization
POST/productsCreate a new productBearer token required (admin only)
GET/products/:productIdRetrieve a product by IDNone required
GET/productsList/filter productsNone required
GET/products/organizations/:organizationIdList products by organizationBearer token required (org member+)
GET/products/:productId/likersList users who liked a productBearer token required (admin only)
PATCH/products/:productIdUpdate a productBearer token required (admin only)
DELETE/products/:productIdDelete a productBearer token required (admin only)
POST/products/:productId/copyCopy an existing productBearer token required (admin only)

File Upload Operations​

MethodPathDescriptionAuthorization
GET/products/:productId/image-upload-urlGet signed URL to upload a product imageBearer token required (admin only)
POST/products/:productId/imagesCreate a product image entryBearer token required (admin only)
DELETE/products/:productId/images/:imageIdDelete a product imageBearer token required (admin only)

Batch Product Operations​

MethodPathDescriptionAuthorization
POST/products/batchCreate multiple productsBearer token required (admin only)
PATCH/products/batchUpdate multiple productsBearer token required (admin only)
DELETE/products/batchDelete multiple productsBearer token required (admin only)
POST/products/batch/copyCopy multiple productsBearer token required (admin only)

Product Variant Operations​

MethodPathDescriptionAuthorization
POST/products/:productId/variantsCreate a product variantBearer token required (admin only)
GET/products/:productId/variantsList variants for a productNone required
GET/products/:productId/variants/:productVariantIdGet a specific variantBearer token required
PATCH/products/:productId/variants/:productVariantIdUpdate a product variantBearer token required (admin only)
DELETE/products/:productId/variants/:productVariantIdDelete a product variantBearer token required (admin only)
POST/product/:productId/variants/bulkCreate multiple variantsBearer token required (admin only)
PATCH/product/:productId/variants/bulkUpdate multiple variantsBearer token required (admin only)
POST/product/:productId/variants/bulk-deleteDelete multiple variantsBearer token required (admin only)

Note: Bulk variant routes intentionally use the singular /product/... prefix (not /products/...).


🗄️ Entity Schema​

The stored product entity combines base fields (auto-generated) with product-specific data including images:

{
"name": "string",
"description": "string",
"images": [
{
"objectId": "string (uuid)",
"type": "string",
"id": "string (uuid)",
"createdAt": "string (datetime)",
"updatedAt": "string (datetime)"
}
],
"createdAt": "string (datetime)",
"id": "string (uuid)",
"updatedAt": "string (datetime)"
}

Field Details​

FieldTypeAuto-GeneratedRequiredDescription
namestring❌✅Product name
descriptionstring❌✅Product description
imagesarrayPartial✅Array of product images
images[].objectIdstring (uuid)❌✅Object identifier for the image file
images[].typestring❌✅Image MIME type (e.g., 'image/png')
images[].idstring (uuid)✅✅Unique identifier for the image entity
images[].createdAtdatetime✅✅Image creation timestamp
images[].updatedAtdatetime✅✅Image last modification timestamp
createdAtdatetime✅✅Product creation timestamp
idstring (uuid)✅✅Unique product identifier (UUID)
updatedAtdatetime✅✅Product last modification timestamp

📝 Note:

  • Auto-generated fields are set by the service and should not be included in create/update requests
  • Each image in the images array gets its own base entity fields (id, createdAt, updatedAt)
  • The objectId in each image references the actual image file in storage
  • API responses normalize each stored image to { type, url }; objectId and image base fields are not returned
  • Field order in responses matches the actual API output

🔐 Authentication Headers​

For protected endpoints, include the following headers:

Authorization: Bearer <admin_access_token>
x-nb-fingerprint: <device_fingerprint>

If the service is configured with authMode: 'cookie', the access token is read from the accessToken cookie instead of the Authorization header.

⚠️ Important: The x-nb-fingerprint header is required for all authenticated requests if fingerprint was specified during authorization. Without it, requests will return 401 Unauthorized.


🔧 API Endpoints​

1. Create Product​

Creates a new product with the provided information.

Request:

  • Method: POST
  • Path: /products
  • Headers: Content-Type: application/json, Authorization: Bearer <access-token>
  • Authorization: Bearer token required (admin only)

Request Body:

FieldTypeRequiredDescription
namestring✅Product name
descriptionstring✅Product description

Response Body:

FieldTypeDescription
idstringUnique product identifier
namestringProduct name
descriptionstringProduct description
imagesarrayNormalized product images ({ type, url }) when present
createdAtstringCreation timestamp
updatedAtstringLast update timestamp

Validation:

  • Schema Validation: Enforced automatically (name, description required)
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X POST {{host}}/products \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '{
"name": "Premium Widget",
"description": "High-quality widget for enterprise use"
}'

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"id": "7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2",
"name": "Premium Widget",
"description": "High-quality widget for enterprise use",
"images": [],
"createdAt": "2024-05-28T09:41:22.552Z",
"updatedAt": "2024-05-28T09:41:22.552Z"
}

Error Responses:

When request body is missing required fields:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
"error": {
"message": "Validation Error",
"data": [
"request body must have required property 'name'",
"request body must have required property 'description'"
]
}
}

When database insert operation fails:

HTTP/1.1 400 Bad Request
Content-Type: application/json

{
"error": {
"message": "Failed to create product"
}
}

2. Get Product by ID​

Retrieves a specific product by their unique ID.

Request:

  • Method: GET
  • Path: /products/:productId
  • Authorization: None required

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Unique product identifier

Response Body:

FieldTypeDescription
idstringUnique product identifier
namestringProduct name
descriptionstringProduct description
imagesarrayNormalized product images ({ type, url }) when present
createdAtstringCreation timestamp
updatedAtstringLast update timestamp

Validation:

  • Schema Validation: Path parameter validation (productId required)
  • Route Validators: None

Example Request:

curl {{host}}/products/7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"id": "7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2",
"name": "Premium Widget",
"description": "High-quality widget for enterprise use",
"images": [],
"createdAt": "2024-05-28T09:41:22.552Z",
"updatedAt": "2024-05-28T09:41:22.552Z"
}

Error Responses:

When no product exists with the provided ID:

HTTP/1.1 404 Not Found
Content-Type: application/json

{
"error": {
"message": "Product not found"
}
}

3. List Products​

Retrieves a list of products with optional filtering and pagination.

Request:

  • Method: GET
  • Path: /products
  • Authorization: None required

Query Parameters:

ParameterTypeRequiredDescription
namestring❌Filter by product name
descriptionstring❌Filter by product description
pagenumber❌Page number for pagination (1-1000)
limitnumber❌Number of items per page (1-50)

Response Body: Paginated envelope { data, metadata.pagination }. Each item includes:

FieldTypeDescription
idstringUnique product identifier
namestringProduct name
descriptionstringProduct description
imagesarrayNormalized product images ({ type, url }) when present
createdAtstringCreation timestamp
updatedAtstringLast update timestamp
metadata.pagination.pagenumberCurrent page number
metadata.pagination.limitnumberItems per page
metadata.pagination.totalnumberTotal number of products
metadata.pagination.totalPagesnumberTotal number of pages
metadata.pagination.hasNextbooleanWhether there are more pages
metadata.pagination.hasPrevbooleanWhether there are previous pages

Validation:

  • Schema Validation: Query parameter validation (optional name, description, page, limit)
  • Route Validators: None

Example Requests:

List all products:

curl {{host}}/products

Filter by name:

curl "{{host}}/products?name=Premium%20Widget"

With pagination:

curl "{{host}}/products?page=1&limit=10"

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"data": [
{
"name": "Premium Widget",
"description": "Updated high-quality widget for enterprise use",
"images": [],
"createdAt": "2025-06-24T06:39:17.101Z",
"id": "3d0e4b74-398c-43e2-a7b4-c9a180477322",
"updatedAt": "2025-06-24T06:39:17.101Z"
},
{
"name": "Product A",
"description": "Updated batch description",
"images": [],
"createdAt": "2025-06-24T06:39:45.378Z",
"id": "0e0dd6c7-c3d5-49e5-a1d8-20daa34d380a",
"updatedAt": "2025-06-24T06:39:55.720Z"
},
{
"name": "Product B",
"description": "Updated batch description",
"images": [],
"createdAt": "2025-06-24T06:39:45.378Z",
"id": "d5a60fdf-2a22-4fcc-8e40-a68cb89cce72",
"updatedAt": "2025-06-24T06:39:55.720Z"
}
],
"metadata": {
"pagination": {
"page": 1,
"limit": 10,
"total": 3,
"totalPages": 1,
"hasNext": false,
"hasPrev": false
}
}
}

4. List Products by Organization ID​

Retrieves a list of products scoped to a specific organization with pagination and image normalization.

Request:

  • Method: GET
  • Path: /products/organizations/:organizationId
  • Authorization: Bearer token required (organization member, admin, or owner)

URL Parameters:

ParameterTypeRequiredDescription
organizationIdstring✅Organization identifier to scope product retrieval

Query Parameters:

ParameterTypeRequiredDescription
pagenumber❌Page number for pagination (1-1000)
limitnumber❌Number of items per page (1-50)

Response Body:

FieldTypeDescription
dataarrayArray of product objects
data[].idstringUnique product identifier
data[].organizationIdstringOrganization identifier
data[].namestringProduct name
data[].descriptionstringProduct description
data[].imagesarrayArray of product images with URLs
data[].createdAtstringCreation timestamp
data[].updatedAtstringLast update timestamp
metadataobjectPagination metadata
metadata.paginationobjectPagination information
metadata.pagination.pagenumberCurrent page number
metadata.pagination.limitnumberItems per page
metadata.pagination.totalnumberTotal number of products
metadata.pagination.totalPagesnumberTotal number of pages
metadata.pagination.hasNextbooleanWhether there are more pages
metadata.pagination.hasPrevbooleanWhether there are previous pages

Validation:

  • Schema Validation: Organization ID path parameter and pagination query parameters
  • Route Validators: Authentication required, organization membership validation
  • Authorization: User must be a member, admin, or owner of the organization

Example Requests:

List products for an organization:

curl -H "Authorization: Bearer eyJ..." "{{host}}/products/organizations/org-123"

With pagination:

curl -H "Authorization: Bearer eyJ..." "{{host}}/products/organizations/org-123?page=1&limit=5"

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"data": [
{
"id": "prod-123",
"organizationId": "org-456",
"name": "Premium Widget",
"description": "High-quality widget for professionals",
"images": [
{
"type": "image/jpeg",
"url": "https://storage.googleapis.com/bucket/abc123..."
}
],
"createdAt": "2024-01-15T10:30:00Z",
"updatedAt": "2024-01-15T10:30:00Z"
}
],
"metadata": {
"pagination": {
"page": 1,
"limit": 5,
"total": 25,
"totalPages": 5,
"hasNext": true,
"hasPrev": false
}
}
}

Error Responses:

  • 401 Unauthorized: Authentication failed
  • 403 Forbidden: Identity is not authorized to access this organization
  • 500 Internal Server Error: Database operation failed or file storage error

Use Cases:

  • Organization-specific product catalogs in multi-tenant applications
  • Admin dashboards for managing products within an organization
  • API consumers retrieving products for a specific business entity

5. List Product Likers​

Retrieves a list of users who have liked a specific product with pagination and avatar normalization.

Request:

  • Method: GET
  • Path: /products/:productId/likers
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Product identifier to retrieve likers for

Query Parameters:

ParameterTypeRequiredDescription
pagenumber❌Page number for pagination (default: 1)
limitnumber❌Number of items per page (default: 10)

Response Body:

FieldTypeDescription
dataarrayArray of user objects who liked the product
data[].idstringUser identifier
data[].namestringUser display name
data[].avatarobjectUser avatar with signed URL ({ type, url }) when present
data[].avatar.typestringMIME type of the avatar image
data[].avatar.urlstringSigned URL for avatar access
metadataobjectPagination metadata
metadata.paginationobjectPagination information
metadata.pagination.pagenumberCurrent page number
metadata.pagination.limitnumberItems per page
metadata.pagination.totalnumberTotal number of likers
metadata.pagination.totalPagesnumberTotal number of pages
metadata.pagination.hasNextbooleanWhether there are more pages
metadata.pagination.hasPrevbooleanWhether there are previous pages

Validation:

  • Schema Validation: Product ID path parameter (pagination query parameters are not schema-validated)
  • Route Validators: Authentication required, admin-only authorization
  • Authorization: User must have admin role

Example Requests:

List product likers:

curl -H "Authorization: Bearer eyJ..." "{{host}}/products/prod-123/likers"

With pagination:

curl -H "Authorization: Bearer eyJ..." "{{host}}/products/prod-123/likers?page=1&limit=5"

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"data": [
{
"id": "user-123",
"name": "John Doe",
"avatar": {
"type": "image/jpeg",
"url": "https://storage.googleapis.com/bucket/abc123..."
}
}
],
"metadata": {
"pagination": {
"page": 1,
"limit": 5,
"total": 25,
"totalPages": 5,
"hasNext": true,
"hasPrev": false
}
}
}

Error Responses:

  • 401 Unauthorized: Authentication failed
  • 403 Forbidden: Identity is not authorized to access this resource
  • 404 Not Found: Product not found
  • 500 Internal Server Error: Database operation failed or file storage error

Use Cases:

  • Admin dashboards showing product engagement metrics
  • Social features displaying who liked products
  • Analytics and reporting on product popularity
  • User relationship analysis for recommendation systems

6. Update Product​

Updates an existing product with partial data.

Request:

  • Method: PATCH
  • Path: /products/:productId
  • Headers: Content-Type: application/json, Authorization: Bearer <access-token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Unique product identifier

Request Body (all fields optional):

FieldTypeRequiredDescription
namestring❌Product name
descriptionstring❌Product description

Response Body:

FieldTypeDescription
idstringUnique product identifier
namestringUpdated product name
descriptionstringUpdated product description
imagesarrayNormalized product images ({ type, url }) when present
createdAtstringCreation timestamp
updatedAtstringLast update timestamp

Validation:

  • Schema Validation: Enforced automatically (partial updates, all fields optional, no additional properties allowed)
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X PATCH {{host}}/products/7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '{"description": "Updated high-quality widget for enterprise use"}'

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"id": "7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2",
"name": "Premium Widget",
"description": "Updated high-quality widget for enterprise use",
"images": [],
"createdAt": "2024-05-28T09:41:22.552Z",
"updatedAt": "2024-05-28T14:22:15.789Z"
}

Error Responses:

When no product exists with the provided ID:

HTTP/1.1 404 Not Found
Content-Type: application/json

{
"error": {
"message": "Product not found"
}
}

7. Delete Product​

Permanently deletes a product from the system.

Request:

  • Method: DELETE
  • Path: /products/:productId
  • Headers: Authorization: Bearer <access-token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Unique product identifier

Response Body:

FieldTypeDescription
No response body-Delete endpoint returns no response body on success

Validation:

  • Schema Validation: Path parameter validation (productId required)
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X DELETE {{host}}/products/7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2 \
-H "Authorization: Bearer <access-token>"

Success Response:

HTTP/1.1 204 No Content

Error Responses:

When no product exists with the provided ID:

HTTP/1.1 404 Not Found
Content-Type: application/json

{
"error": {
"message": "Product not found"
}
}

8. Copy Product​

Creates a copy of an existing product with a new ID.

Request:

  • Method: POST
  • Path: /products/:productId/copy
  • Headers: Authorization: Bearer <access-token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅ID of product to copy

Response Body:

FieldTypeDescription
idstringUnique identifier for the copied product
namestringProduct name (copied from original)
descriptionstringProduct description (copied from original)
imagesarrayNormalized product images ({ type, url }) when present
createdAtstringCreation timestamp
updatedAtstringLast update timestamp

Validation:

  • Schema Validation: Path parameter validation (productId required)
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X POST {{host}}/products/7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2/copy \
-H "Authorization: Bearer <access-token>"

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"id": "9abc123f-1cd8-4def-b123-456789abcdef",
"name": "Premium Widget",
"description": "High-quality widget for enterprise use",
"images": [],
"createdAt": "2024-05-28T15:30:45.123Z",
"updatedAt": "2024-05-28T15:30:45.123Z"
}

Error Responses:

When no product exists with the provided ID:

HTTP/1.1 404 Not Found
Content-Type: application/json

{
"error": {
"message": "Product not found"
}
}

🔄 Batch Product Operations​

The Product Service provides powerful batch operations for managing multiple products efficiently.

9. Create Multiple Products​

Creates multiple products in a single request.

Request:

  • Method: POST
  • Path: /products/batch
  • Headers: Content-Type: application/json, Authorization: Bearer <access-token>
  • Authorization: Bearer token required (admin only)

Request Body: Array of product objects, each requiring name and description.

Response Body:

FieldTypeDescription
idstringUnique product identifier
namestringProduct name
descriptionstringProduct description
createdAtstringCreation timestamp
updatedAtstringLast update timestamp

Validation:

  • Schema Validation: Enforced automatically (array of product objects with required name and description)
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X POST {{host}}/products/batch \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '[
{
"name": "Product A",
"description": "Description for Product A"
},
{
"name": "Product B",
"description": "Description for Product B"
}
]'

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

[
{
"name": "Product A",
"description": "Description for Product A",
"createdAt": "2025-06-24T06:39:45.378Z",
"id": "0e0dd6c7-c3d5-49e5-a1d8-20daa34d380a",
"updatedAt": "2025-06-24T06:39:45.378Z"
},
{
"name": "Product B",
"description": "Description for Product B",
"createdAt": "2025-06-24T06:39:45.378Z",
"id": "d5a60fdf-2a22-4fcc-8e40-a68cb89cce72",
"updatedAt": "2025-06-24T06:39:45.378Z"
}
]

10. Update Multiple Products​

Updates multiple products with the same data in a single request.

Request:

  • Method: PATCH
  • Path: /products/batch
  • Headers: Content-Type: application/json, Authorization: Bearer <access-token>
  • Authorization: Bearer token required (admin only)

Request Body:

FieldTypeRequiredDescription
idsarray of strings✅Array of product IDs to update
dataobject✅Update data to apply to all products
data.namestring❌New product name
data.descriptionstring❌New product description

Response Body:

FieldTypeDescription
idstringUnique product identifier
namestringUpdated product name
descriptionstringUpdated product description
imagesarrayNormalized product images ({ type, url }) when present
createdAtstringCreation timestamp
updatedAtstringLast update timestamp

Validation:

  • Schema Validation: Enforced automatically (ids array and data object required)
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X PATCH {{host}}/products/batch \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '{
"ids": [
"7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2",
"8fec096b-1bc7-5bfe-c827-3600e8fe2790"
],
"data": {
"description": "Updated batch description"
}
}'

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

[
{
"id": "7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2",
"name": "Product A",
"description": "Updated batch description",
"images": [],
"createdAt": "2024-05-28T09:41:22.552Z",
"updatedAt": "2024-05-28T15:45:12.789Z"
},
{
"id": "8fec096b-1bc7-5bfe-c827-3600e8fe2790",
"name": "Product B",
"description": "Updated batch description",
"images": [],
"createdAt": "2024-05-28T09:41:23.123Z",
"updatedAt": "2024-05-28T15:45:12.789Z"
}
]

11. Delete Multiple Products​

Deletes multiple products in a single request.

Request:

  • Method: DELETE
  • Path: /products/batch
  • Headers: Content-Type: application/json, Authorization: Bearer <access-token>
  • Authorization: Bearer token required (admin only)

Request Body: Array of product IDs to delete.

Response Body:

FieldTypeDescription
No response body-Delete endpoint returns no response body on success

Validation:

  • Schema Validation: Enforced automatically (array of strings)
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X DELETE {{host}}/products/batch \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '[
"7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2",
"8fec096b-1bc7-5bfe-c827-3600e8fe2790"
]'

Success Response:

HTTP/1.1 204 No Content

12. Copy Multiple Products​

Creates copies of multiple products in a single request.

Request:

  • Method: POST
  • Path: /products/batch/copy
  • Headers: Content-Type: application/json, Authorization: Bearer <access-token>
  • Authorization: Bearer token required (admin only)

Request Body: Array of product IDs to copy.

Response Body:

FieldTypeDescription
idstringUnique identifier for the copied product
namestringProduct name (copied from original)
descriptionstringProduct description (copied from original)
imagesarrayNormalized product images ({ type, url }) when present
createdAtstringCreation timestamp
updatedAtstringLast update timestamp

Validation:

  • Schema Validation: Enforced automatically (array of strings)
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X POST {{host}}/products/batch/copy \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '[
"7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2",
"8fec096b-1bc7-5bfe-c827-3600e8fe2790"
]'

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

[
{
"id": "9abc123f-1cd8-4def-b123-456789abcdef",
"name": "Product A",
"description": "Description for Product A",
"images": [],
"createdAt": "2024-05-28T16:00:00.000Z",
"updatedAt": "2024-05-28T16:00:00.000Z"
},
{
"id": "def456a1-2e3f-4567-8901-23456789bcde",
"name": "Product B",
"description": "Description for Product B",
"images": [],
"createdAt": "2024-05-28T16:00:00.100Z",
"updatedAt": "2024-05-28T16:00:00.100Z"
}
]

13. Get Product Image Upload URL​

Generates a pre-signed URL for securely uploading a product image. Returns an object ID and a temporary signed URL.

Request:

  • Method: GET
  • Path: /products/:productId/image-upload-url
  • Headers: Authorization: Bearer <token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Target product ID

Query Parameters:

ParameterTypeRequiredDescription
contentTypestring✅One of image/jpeg, image/png, image/webp, image/gif, image/svg+xml, image/avif, image/bmp, image/x-icon, image/tiff, image/heif, or image/heic
contentLengthnumber✅File size in bytes (max 10MB)

Response Body:

FieldTypeDescription
objectIdstringGenerated storage object ID for the product image
urlstringPre-signed URL for uploading the file

Validation:

  • Schema Validation: Uses image upload schema (content type and size constraints)
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl "{{host}}/products/7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2/image-upload-url?contentType=image/webp&contentLength=2097152" \
-H "Authorization: Bearer <access-token>"

Success Response:

{
"objectId": "7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2",
"url": "https://storage.googleapis.com/bucket/products/...&X-Goog-Expires=900&X-Goog-Signature=..."
}

14. Create Product Image​

Creates a product image entry after uploading the file to cloud storage.

Request:

  • Method: POST
  • Path: /products/:productId/images
  • Headers:
    • Content-Type: application/json
    • Authorization: Bearer <token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Target product ID

Request Body:

FieldTypeRequiredDescription
objectIdstring✅Storage object ID from upload URL
typestring✅Image type/category

Response Body:

FieldTypeDescription
typestringImage MIME type
urlstringSigned URL for the uploaded image

Example Request:

curl -X POST "{{host}}/products/7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2/images" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '{
"objectId": "image-uuid-123",
"type": "product-image"
}'

Success Response:

HTTP/1.1 201 Created
Content-Type: application/json

{
"type": "image/webp",
"url": "https://storage.googleapis.com/bucket/products/...&X-Goog-Expires=900&X-Goog-Signature=..."
}

15. Delete Product Image​

Removes a product image and deletes the file from cloud storage.

Request:

  • Method: DELETE
  • Path: /products/:productId/images/:imageId
  • Headers: Authorization: Bearer <token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Target product ID
imageIdstring✅Image ID to delete

Example Request:

curl -X DELETE "{{host}}/products/7edfb95f-0ab6-4adc-a6e1-2a86a2f1e6d2/images/image-uuid-123" \
-H "Authorization: Bearer <access-token>"

16. Create Product Variant​

Creates a new variant for an existing product.

Request:

  • Method: POST
  • Path: /products/:productId/variants
  • Headers:
    • Content-Type: application/json
    • Authorization: Bearer <token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Parent product ID

Request Body:

FieldTypeRequiredDescription
titlestring✅Product variant title
descriptionstring❌Variant description
skustring❌Stock keeping unit identifier
imageIdsarray of strings❌Array of image identifiers associated with the variant
priceobject❌Pricing details (see below)

Price Object:

FieldTypeRequiredDescription
amountnumber❌Price amount
currencystring❌ISO currency code
taxIncludedboolean❌Whether tax is included in amount
taxableboolean❌Whether the variant is taxable

Response Body:

FieldTypeDescription
idstringUnique variant identifier
productIdstringParent product ID
titlestringVariant title
descriptionstringVariant description
skustringStock keeping unit
imageIdsarrayArray of image IDs
priceobjectPricing information
createdAtstringCreation timestamp
updatedAtstringLast update timestamp

Validation:

  • Schema Validation: Request body requires title; all other fields optional
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X POST "{{host}}/products/product-123/variants" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '{
"title": "Large Size",
"description": "Large variant of the product",
"sku": "PROD-LG-001",
"price": {
"amount": 29.99,
"currency": "USD",
"taxIncluded": false,
"taxable": true
}
}'

Success Response:

HTTP/1.1 201 Created
Content-Type: application/json

{
"id": "variant-456",
"productId": "product-123",
"title": "Large Size",
"description": "Large variant of the product",
"sku": "PROD-LG-001",
"price": {
"amount": 29.99,
"currency": "USD",
"taxIncluded": false,
"taxable": true
},
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:30:00.000Z"
}

17. List Product Variants​

Retrieves all variants for a specific product with pagination.

Request:

  • Method: GET
  • Path: /products/:productId/variants
  • Authorization: None required

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Parent product ID

Query Parameters:

ParameterTypeRequiredDescription
pagenumber❌Page number for pagination (1-1000)
limitnumber❌Number of results per page (1-50)

Response Body: Paginated response with variants array and metadata.

Response Structure:

{
"data": [
{
"id": "string",
"productId": "string",
"title": "string",
"description": "string",
"sku": "string",
"imageIds": ["string"],
"price": {
"amount": number,
"currency": "string",
"taxIncluded": boolean,
"taxable": boolean
},
"createdAt": "string",
"updatedAt": "string"
}
],
"metadata": {
"pagination": {
"page": number,
"limit": number,
"total": number,
"totalPages": number,
"hasNext": boolean,
"hasPrev": boolean
}
}
}

Validation:

  • Schema Validation: Path parameter validation and pagination query parameters
  • Route Validators: None

Example Request:

curl "{{host}}/products/product-123/variants?page=1&limit=20"

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"data": [
{
"id": "variant-456",
"productId": "product-123",
"title": "Large Size",
"description": "Large variant",
"sku": "PROD-LG-001",
"imageIds": [],
"price": {
"amount": 29.99,
"currency": "USD",
"taxIncluded": false,
"taxable": true
},
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:30:00.000Z"
}
],
"metadata": {
"pagination": {
"page": 1,
"limit": 20,
"total": 5,
"totalPages": 1,
"hasNext": false,
"hasPrev": false
}
}
}

18. Get Product Variant​

Retrieves a specific product variant by ID.

Request:

  • Method: GET
  • Path: /products/:productId/variants/:productVariantId
  • Headers: Authorization: Bearer <token>
  • Authorization: Bearer token required

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Parent product ID
productVariantIdstring✅Variant ID

Response Body:

FieldTypeDescription
idstringUnique variant identifier
productIdstringParent product ID
titlestringVariant title
descriptionstringVariant description
skustringStock keeping unit
imageIdsarrayArray of image IDs
priceobjectPricing information
createdAtstringCreation timestamp
updatedAtstringLast update timestamp

Validation:

  • Schema Validation: Path parameter validation (productId and productVariantId required)
  • Route Validators:
    • Require authenticated request (bearer token)

Example Request:

curl "{{host}}/products/product-123/variants/variant-456" \
-H "Authorization: Bearer <access-token>"

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"id": "variant-456",
"productId": "product-123",
"title": "Large Size",
"description": "Large variant of the product",
"sku": "PROD-LG-001",
"imageIds": [],
"price": {
"amount": 29.99,
"currency": "USD",
"taxIncluded": false,
"taxable": true
},
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:30:00.000Z"
}

Error Responses:

  • 401 Unauthorized: Authentication failed
  • 404 Not Found: Product variant not found
  • 500 Internal Server Error: Database operation failed

19. Update Product Variant​

Updates an existing product variant with partial data.

Request:

  • Method: PATCH
  • Path: /products/:productId/variants/:productVariantId
  • Headers:
    • Content-Type: application/json
    • Authorization: Bearer <token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Parent product ID
productVariantIdstring✅Variant ID to update

Request Body (all fields optional):

FieldTypeRequiredDescription
titlestring❌Product variant title
descriptionstring❌Variant description
skustring❌Stock keeping unit identifier
imageIdsarray of strings❌Array of image identifiers associated with the variant
priceobject❌Pricing details (see below)

Price Object (all fields optional):

FieldTypeRequiredDescription
amountnumber❌Price amount
currencystring❌ISO currency code
taxIncludedboolean❌Whether tax is included in amount
taxableboolean❌Whether the variant is taxable

Response Body:

FieldTypeDescription
idstringUnique variant identifier
productIdstringParent product ID
titlestringUpdated variant title
descriptionstringUpdated variant description
skustringUpdated stock keeping unit
imageIdsarrayUpdated array of image IDs
priceobjectUpdated pricing information
createdAtstringCreation timestamp
updatedAtstringLast update timestamp

Validation:

  • Schema Validation: All fields optional; no additional properties allowed
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X PATCH "{{host}}/products/product-123/variants/variant-456" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '{
"price": {
"amount": 34.99,
"currency": "USD"
}
}'

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

{
"id": "variant-456",
"productId": "product-123",
"title": "Large Size",
"description": "Large variant of the product",
"sku": "PROD-LG-001",
"imageIds": [],
"price": {
"amount": 34.99,
"currency": "USD",
"taxIncluded": false,
"taxable": true
},
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T14:22:15.789Z"
}

Error Responses:

  • 401 Unauthorized: Authentication failed
  • 403 Forbidden: Identity is not authorized to access this resource
  • 404 Not Found: Product variant not found
  • 500 Internal Server Error: Database operation failed

20. Delete Product Variant​

Removes a product variant.

Request:

  • Method: DELETE
  • Path: /products/:productId/variants/:productVariantId
  • Headers: Authorization: Bearer <token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Parent product ID
productVariantIdstring✅Variant ID to delete

Response Body: No response body (204 No Content)

Validation:

  • Schema Validation: Path parameter validation (productId and productVariantId required)
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X DELETE "{{host}}/products/product-123/variants/variant-456" \
-H "Authorization: Bearer <access-token>"

Success Response:

HTTP/1.1 204 No Content

Error Responses:

  • 401 Unauthorized: Authentication failed
  • 403 Forbidden: Identity is not authorized to access this resource
  • 500 Internal Server Error: Database operation failed

21. Create Product Variants in Bulk​

Creates multiple product variants at once (1-100 variants per request).

Request:

  • Method: POST
  • Path: /product/:productId/variants/bulk
  • Headers:
    • Content-Type: application/json
    • Authorization: Bearer <token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Parent product ID

Request Body: Array of variant objects (1-100 items), each requiring title.

FieldTypeRequiredDescription
titlestring✅Product variant title
descriptionstring❌Variant description
skustring❌Stock keeping unit identifier
imageIdsarray of strings❌Array of image identifiers
priceobject❌Pricing details

Response Body: Array of created variant objects.

Validation:

  • Schema Validation: Array of variant objects (1-100 items), each requires title
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X POST "{{host}}/product/product-123/variants/bulk" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '[
{
"title": "Small Size",
"sku": "PROD-SM-001",
"price": {
"amount": 19.99,
"currency": "USD"
}
},
{
"title": "Medium Size",
"sku": "PROD-MD-001",
"price": {
"amount": 24.99,
"currency": "USD"
}
}
]'

Success Response:

HTTP/1.1 201 Created
Content-Type: application/json

[
{
"id": "variant-789",
"productId": "product-123",
"title": "Small Size",
"sku": "PROD-SM-001",
"createdAt": "2025-01-15T10:30:00.000Z",
"updatedAt": "2025-01-15T10:30:00.000Z"
},
{
"id": "variant-790",
"productId": "product-123",
"title": "Medium Size",
"sku": "PROD-MD-001",
"createdAt": "2025-01-15T10:30:00.100Z",
"updatedAt": "2025-01-15T10:30:00.100Z"
}
]

22. Update Product Variants in Bulk​

Updates multiple product variants at once with the same data (1-100 variants per request).

Request:

  • Method: PATCH
  • Path: /product/:productId/variants/bulk
  • Headers:
    • Content-Type: application/json
    • Authorization: Bearer <token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Parent product ID

Request Body:

FieldTypeRequiredDescription
idsarray of strings✅Array of variant IDs to update (1-100 items)
dataobject✅Update data to apply to all variants (see below)

Data Object (all fields optional):

FieldTypeRequiredDescription
titlestring❌Product variant title
descriptionstring❌Variant description
skustring❌Stock keeping unit identifier
imageIdsarray of strings❌Array of image identifiers
priceobject❌Pricing details

Response Body: Array of updated variant objects.

Validation:

  • Schema Validation: ids array (1-100 items) and data object required
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X PATCH "{{host}}/product/product-123/variants/bulk" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '{
"ids": ["variant-456", "variant-789"],
"data": {
"price": {
"amount": 29.99,
"currency": "USD"
}
}
}'

Success Response:

HTTP/1.1 200 OK
Content-Type: application/json

[
{
"id": "variant-456",
"productId": "product-123",
"title": "Large Size",
"price": {
"amount": 29.99,
"currency": "USD"
},
"updatedAt": "2025-01-15T14:22:15.789Z"
},
{
"id": "variant-789",
"productId": "product-123",
"title": "Small Size",
"price": {
"amount": 29.99,
"currency": "USD"
},
"updatedAt": "2025-01-15T14:22:15.789Z"
}
]

Error Responses:

  • 401 Unauthorized: Authentication failed
  • 403 Forbidden: Identity is not authorized to access this resource
  • 404 Not Found: One or more product variants not found
  • 500 Internal Server Error: Database operation failed

23. Delete Product Variants in Bulk​

Deletes multiple product variants at once (1-100 variants per request).

Request:

  • Method: POST
  • Path: /product/:productId/variants/bulk-delete
  • Headers:
    • Content-Type: application/json
    • Authorization: Bearer <token>
  • Authorization: Bearer token required (admin only)

URL Parameters:

ParameterTypeRequiredDescription
productIdstring✅Parent product ID

Request Body: Array of variant IDs to delete (1-100 items).

Response Body: No response body (204 No Content)

Validation:

  • Schema Validation: Array of variant ID strings (1-100 items)
  • Route Validators:
    • Require authenticated request (bearer token)
    • Require admin role

Example Request:

curl -X POST "{{host}}/product/product-123/variants/bulk-delete" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <access-token>" \
-d '["variant-456", "variant-789"]'

Success Response:

HTTP/1.1 204 No Content

Error Responses:

  • 401 Unauthorized: Authentication failed
  • 403 Forbidden: Identity is not authorized to access this resource
  • 500 Internal Server Error: Database operation failed

⚙️ Configuration Options​

Datastore​

CollectionRequiredDescription
products✅Product documents
identities✅Identity lookups / auth context
profiles✅Product liker profiles
productVariants❌Required only for product variant endpoints
organizations❌Required for /products/organizations/:organizationId

The organization-scoped endpoint also requires configuration.organization.roles; its keys must include owner, admin, and member.

Service Configuration​

interface ProductServiceConfiguration {
authSecrets: {
authEncSecret: string; // JWT encryption secret
authSignSecret: string; // JWT signing secret
};
authMode?: 'bearer' | 'cookie'; // Default: bearer behavior when unset
identity?: {
typeIds?: {
admin: string; // Admin user type identifier
guest: string; // Guest user type identifier
regular: string; // Regular user type identifier
};
};
organization?: {
roles?: {
admin: string;
member: string;
owner: string;
};
};
}

Configuration Details​

The product service configuration is organized into logical groups for security and user type management.

🔐 Security Settings​

authSecrets - JWT token security secrets

  • Type: { authEncSecret: string; authSignSecret: string }
  • Description: Secret keys for JWT encryption and signing (used for token validation)
  • Required: Yes (for production)
  • Child Properties:
    • authEncSecret: Secret key for JWT payload encryption
    • authSignSecret: Secret key for JWT signature verification

👥 User Type Settings​

identity.typeIds - User type identifier configuration

  • Type: { admin?: string; guest?: string; regular?: string }
  • Description: Custom user type identifiers for role-based access control
  • Default: undefined (uses default type validation)
  • Child Properties:
    • admin: Admin user type identifier
      • Type: string
      • Description: Custom identifier for admin users
      • Use Case: Role-based access control for administrative operations
      • Example: "admin", "administrator", "superuser"
    • guest: Guest user type identifier
      • Type: string
      • Description: Custom identifier for guest users
      • Use Case: Limited access for unauthenticated or temporary users
      • Example: "guest", "visitor", "anonymous"
    • regular: Regular user type identifier
      • Type: string
      • Description: Custom identifier for regular users
      • Use Case: Standard user access permissions
      • Example: "user", "member", "customer"

Example Configuration​

const productConfig = {
authSecrets: {
authEncSecret: process.env.AUTH_ENC_SECRET || 'your-enc-secret',
authSignSecret: process.env.AUTH_SIGN_SECRET || 'your-sign-secret',
},
identity: {
typeIds: {
admin: '100',
guest: '000',
regular: '001',
},
},
organization: {
roles: {
admin: 'admin',
member: 'member',
owner: 'owner',
},
},
};

🚨 Error Handling​

All product service errors return JSON format with appropriate HTTP status codes:

Common Error Codes​

StatusError MessageDescription
400Validation ErrorInvalid request body format or missing required fields
400Failed to create productDatabase insert operation failed to return an inserted ID
400Failed to update productUpdate operation doesn't modify any data (no changes detected)
400Failed to create productsBatch creation operation failed
400Failed to update productsBatch update operation failed
400Failed to delete productsBatch deletion operation failed
400Failed to copy productProduct copy operation failed
400Failed to copy productsBatch copy operation failed
401token could not be verifiedMissing or invalid authorization token
403Identity is not authorized to access this resourceIdentity lacks required type permissions (for example, admin access)
403Identity is not authorized to access this organizationIdentity lacks a permitted organization role
404Product not foundProduct doesn't exist for the requested operation
500Failed to create productDatabase connection issues or unexpected failures during creation
500Failed to get productDatabase connection issues or unexpected failures during retrieval
500Failed to find productsDatabase connection issues, invalid filter syntax, or unexpected failures during listing
500Failed to update productDatabase connection issues or unexpected failures during update
500Failed to delete productDatabase connection issues or unexpected failures during deletion
500Failed to copy productDatabase connection issues or unexpected failures during copying

Error Response Format​

{
"error": {
"message": "Error message description",
"data": ["Additional error details"]
}
}

Validation Errors include additional details:

{
"error": {
"message": "Validation Error",
"data": [
"request body must have required property 'name'",
"request body must have required property 'description'"
]
}
}