Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.heylua.ai/llms.txt

Use this file to discover all available pages before exploring further.

Overview

The Products API provides complete CRUD operations for managing an e-commerce product catalog. Returns ProductInstance objects with direct property access.
import { Products } from 'lua-cli';

// Search products - returns ProductSearchInstance
const results = await Products.search('laptop');

// Direct property access on results
const names = results.map(p => p.name);
const prices = results.map(p => p.price);

// Create product - returns ProductInstance
const product = await Products.create({
  name: 'MacBook Pro',
  price: 1999.99
});

// Direct property access
console.log(product.name);   // "MacBook Pro"
console.log(product.price);  // 1999.99

// Instance methods
await product.update({ price: 1799.99 });
await product.save();
await product.delete();

Direct Access

Access properties with product.name not product.data.name

Array Methods

Use .map(), .filter() on search results

Instance Methods

Built-in update(), save(), and delete()

Iteration

Use for...of loops

Return Shape Reference

Products.search() and Products.get() do NOT return arrays with a .data property. They return instance wrappers that are directly iterable.
MethodReturnsHow to use
Products.search(q, limit?)ProductSearchInstance — iterable, has .products, .lengthresults.map(p => p.name), results.products, results.lengthno .data
Products.get(page?, limit?) or Products.get({page, limit, filter})ProductPaginationInstance — iterable, has .products, .pagination, .lengthresults.map(p => p.name), results.pagination.totalPagesno .data
Products.create(product)ProductInstanceproduct.name, product.price, await product.update({...})
Products.getById(id)ProductInstanceDirect property access
Products.update(data, id)UpdateProductResponse{ product }
Products.delete(id)DeleteProductResponse{ success }
Quick examples:
// ✅ Products.search → ProductSearchInstance (iterable)
const results = await Products.search('laptop');
results.forEach(p => console.log(p.name, p.price));  // iterate directly
const names = results.map(p => p.name);              // array methods work
console.log(results.length);                          // ✅ count
// ❌ results.data   → does not exist

// ✅ Products.get → ProductPaginationInstance
const page = await Products.get({ page: 1, limit: 20, filter: { inStock: true } });
page.map(p => p.name);                               // iterate directly
console.log(page.pagination.totalPages);             // ✅ pagination
// ❌ page.data      → does not exist

Methods

Search products by name or description using semantic search.
Products.search(query: string): Promise<ProductSearchInstance>
query
string
required
Search query to match against product names and descriptions
Returns: ProductSearchInstance - Array-like collection with direct access to products via .products property Example:
// Search with default limit (5)
const results = await Products.search('laptop');

// Search with custom limit
const moreResults = await Products.search('laptop', 10);

// Direct array methods
results.forEach(product => {
  console.log(`${product.name} - $${product.price}`);
});

// Or use .map()
const names = results.map(p => p.name);
const prices = results.map(p => p.price);

// Filter
const affordable = results.filter(p => p.price < 1000);

// Find
const specific = results.find(p => p.sku === 'LAP-001');

// Check length
console.log(`Found ${results.length} products`);

// For...of iteration
for (const product of results) {
  console.log(product.name);
}

get()

Retrieve products with pagination and optional filtering.
// Simple pagination (backward compatible)
Products.get(page?: number, limit?: number): Promise<ProductPaginationInstance>

// With filter options (recommended)
Products.get(options?: ProductFilterOptions): Promise<ProductPaginationInstance>
ProductFilterOptions:
interface ProductFilterOptions {
  page?: number;      // Page number (1-indexed), default: 1
  limit?: number;     // Items per page, default: 10
  filter?: object;    // MongoDB-style filter for product data
}
page
number
default:1
Page number (1-indexed)
limit
number
default:10
Number of products per page
filter
object
MongoDB-style filter object to query product data fields
Returns: ProductPaginationInstance - Array-like collection with pagination support Examples:
// Simple pagination (backward compatible)
const page = await Products.get(1, 20);

// Using options object (recommended)
const page = await Products.get({ page: 1, limit: 20 });

// With filters - exact match
const electronics = await Products.get({
  page: 1,
  limit: 20,
  filter: { category: 'Electronics' }
});

// With filters - price range
const affordable = await Products.get({
  filter: {
    price: { $lte: 100 }
  }
});

// With filters - multiple conditions
const inStockLaptops = await Products.get({
  filter: {
    category: 'Laptops',
    inStock: true,
    price: { $gte: 500, $lte: 1500 }
  }
});

// Direct array methods
page.forEach(product => {
  console.log(`${product.name} - $${product.price}`);
});

// Access pagination info
console.log(`Page ${page.pagination.currentPage} of ${page.pagination.totalPages}`);
console.log(`Showing ${page.length} of ${page.pagination.totalCount} products`);

// Navigate pages
if (page.pagination.hasNextPage) {
  const nextPage = await page.nextPage();
}

if (page.pagination.hasPrevPage) {
  const prevPage = await page.prevPage();
}

// Use array methods
const names = page.map(p => p.name);
const inStock = page.filter(p => p.inStock);
Filter Operators: The filter supports MongoDB-style operators:
// Comparison
{ price: { $eq: 99.99 } }      // Equal
{ price: { $ne: 99.99 } }      // Not equal
{ price: { $gt: 50 } }         // Greater than
{ price: { $gte: 50 } }        // Greater than or equal
{ price: { $lt: 100 } }        // Less than
{ price: { $lte: 100 } }       // Less than or equal

// Array matching
{ category: { $in: ['Electronics', 'Computers'] } }

// Nested fields (dot notation)
{ 'specs.color': 'black' }
{ 'metadata.brand': 'Apple' }

getById()

Get a specific product by ID.
Products.getById(id: string): Promise<ProductInstance>
Returns: ProductInstance with direct property access and methods Example:
const product = await Products.getById('product_abc123');

// Direct property access
console.log(product.name);   // "MacBook Pro"
console.log(product.price);  // 1999.99
console.log(product.sku);    // "MBP-14"

// Instance methods
await product.update({ price: 1799.99 });
await product.delete();

create()

Create a new product.
Products.create(product: Product): Promise<ProductInstance>
product.name
string
required
Product name
product.price
number
required
Product price
product.category
string
Product category
product.sku
string
Stock keeping unit
product.inStock
boolean
default:true
Whether product is in stock
product.description
string
Product description
Returns: ProductInstance with direct property access and methods Example:
const product = await Products.create({
  name: 'Wireless Mouse',
  price: 29.99,
  category: 'Electronics',
  sku: 'MOUSE-001',
  inStock: true,
  description: 'Ergonomic wireless mouse'
});

// Direct property access
console.log(product.id);      // "product_xyz789"
console.log(product.name);    // "Wireless Mouse"
console.log(product.price);   // 29.99

// Instance methods available
await product.update({ price: 24.99 });

update() (Instance Method)

Update an existing product via an instance. Use product.update() on a retrieved product — there is no static Products.update() method.
product.update(data: Record<string, any>): Promise<Product>
data
object
required
Partial product data to update
Example:
const product = await Products.getById('product_xyz789');

// Update via instance method
await product.update({
  price: 24.99,
  inStock: false
});

// Access updated properties
console.log(product.price);   // 24.99
console.log(product.inStock); // false

save() (Instance Method)

Save the current state of the product to the server. This is a convenience method that persists all changes made to the product instance.
product.save(): Promise<boolean>
Returns: Promise resolving to true if successful Example:
const product = await Products.getById('product_xyz789');

// Modify product properties directly
product.price = 24.99;
product.inStock = false;
product.description = "Updated description";

// Save all changes at once
await product.save();

// Much cleaner workflow!
New in Latest Version: The save() method provides a simpler workflow - modify properties then save, rather than calling Products.update() with the product ID.

delete()

Delete a product.
Products.delete(id: string): Promise<DeleteProductResponse>
Example:
await Products.delete('product_xyz789');

ProductInstance

All product methods return ProductInstance objects with: Direct Property Access:
product.name
product.price
product.sku
product.inStock
product.category
product.description
// Any custom fields
Instance Methods:
await product.update({ price: 999.99 });
await product.save();
await product.delete();
Backward Compatible:
product.name;        // ✅ New way
product.data.name;   // ✅ Old way still works

Complete Examples

Search Tool

import { LuaTool, Products } from 'lua-cli';
import { z } from 'zod';

export class SearchProductsTool implements LuaTool {
  name = "search_products";
  description = "Search for products by name or description";
  
  inputSchema = z.object({
    query: z.string().describe("Search query"),
    maxPrice: z.number().optional()
  });

  async execute(input: z.infer<typeof this.inputSchema>) {
    const results = await Products.search(input.query);
    
    // Filter by price if specified
    let products = results.products;
    if (input.maxPrice) {
      products = products.filter(p => p.price <= input.maxPrice);
    }
    
    return {
      products: products.map(p => ({
        id: p.id,
        name: p.name,
        price: `$${p.price.toFixed(2)}`,
        inStock: p.inStock ? '✅ In Stock' : '❌ Out of Stock'
      })),
      total: products.length
    };
  }
}

Create Product Tool

export class CreateProductTool implements LuaTool {
  name = "create_product";
  description = "Add a new product to the catalog";
  
  inputSchema = z.object({
    name: z.string(),
    price: z.number().positive(),
    category: z.string().optional(),
    sku: z.string().optional(),
    description: z.string().optional()
  });

  async execute(input: z.infer<typeof this.inputSchema>) {
    const result = await Products.create({
      ...input,
      inStock: true
    });
    
    return {
      success: true,
      productId: result.id,
      message: `Product "${input.name}" created successfully`
    };
  }
}

Update Stock Tool

export class UpdateStockTool implements LuaTool {
  name = "update_stock";
  description = "Update product stock status";
  
  inputSchema = z.object({
    productId: z.string(),
    inStock: z.boolean()
  });

  async execute(input: z.infer<typeof this.inputSchema>) {
    await Products.update(
      { inStock: input.inStock },
      input.productId
    );
    
    return {
      success: true,
      message: `Stock status updated to ${input.inStock ? 'in stock' : 'out of stock'}`
    };
  }
}

Use Cases

E-commerce Catalog

// Browse products with pagination
const products = await Products.get({ page: 1, limit: 20 });

// Search specific items (semantic search)
const laptops = await Products.search('laptop');

// Filter by category
const electronics = await Products.get({
  filter: { category: 'Electronics' }
});

// Get product details
const product = await Products.getById(laptops.products[0].id);

Inventory Management

// Update product price
await Products.update({ price: 899.99 }, productId);

// Mark as out of stock
await Products.update({ inStock: false }, productId);

// Update multiple fields
await Products.update({
  price: 799.99,
  inStock: true,
  category: 'Electronics - Sale'
}, productId);

// Get all out-of-stock products
const outOfStock = await Products.get({
  filter: { inStock: false }
});

Filtering Products

// By price range
const affordable = await Products.get({
  filter: {
    price: { $gte: 50, $lte: 200 }
  }
});

// By category and availability
const availablePhones = await Products.get({
  filter: {
    category: 'Phones',
    inStock: true
  }
});

// By nested properties
const appleProducts = await Products.get({
  filter: {
    'metadata.brand': 'Apple'
  }
});

// Multiple categories
const gadgets = await Products.get({
  filter: {
    category: { $in: ['Phones', 'Tablets', 'Watches'] }
  }
});

Product Recommendations

// Search similar products
const similar = await Products.search(product.category);

// Or filter by same category
const sameCategory = await Products.get({
  filter: {
    category: product.category,
    price: {
      $gte: product.price * 0.8,
      $lte: product.price * 1.2
    }
  }
});

Search vs Filter

Best Practices

Always check if product exists:
const product = await Products.getById(input.productId);

if (!product) {
  throw new Error(`Product not found: ${input.productId}`);
}
const product = await Products.getById(productId);

if (!product.inStock) {
  return {
    success: false,
    message: `${product.name} is currently out of stock`
  };
}
return {
  products: products.map(p => ({
    ...p,
    price: `$${p.price.toFixed(2)}`,
    formattedPrice: new Intl.NumberFormat('en-US', {
      style: 'currency',
      currency: 'USD'
    }).format(p.price)
  }))
};
const results = await Products.search(query);

if (results.products.length === 0) {
  return {
    products: [],
    message: `No products found for "${query}". Try different search terms.`
  };
}
If product results don’t look right, log the raw return to see the actual shape:
const results = await Products.search(input.query);
console.log('Products.search result:', JSON.stringify(results, null, 2));
Then run lua logs --type skill --limit 5 after a test message to inspect the output. See the Debugging Skills guide for the full workflow.

Next Steps

Baskets API

Add products to shopping carts

Product Examples

See complete tool examples

Debugging Skills

Inspect runtime return values