Other SDK Classes 
Beyond Nimbu.Object and Nimbu.Query, the Nimbu SDK provides specialized classes for working with specific data types and features. These classes extend the base Nimbu.Object functionality with domain-specific methods.
Nimbu.Customer 
The Customer class represents customer accounts in your Nimbu site. Customers can log in, make purchases, and have associated data like orders and favorites.
Creating Customers 
// Create a new customer
const customer = new Nimbu.Customer();
customer.set('email', '[email protected]');
customer.set('name', 'John Doe');
customer.set('password', 'securePassword123');
await customer.save();
console.log('Customer created:', customer.id);Finding Customers 
// Find customer by email
const query = new Nimbu.Query('customers');
query.equalTo('email', '[email protected]');
const customer = await query.first();
if (customer) {
  console.log('Found customer:', customer.get('name'));
}Customer Properties 
// Access customer data
const email = customer.get('email');
const name = customer.get('name');
const createdAt = customer.createdAt;
// Check customer status
const isActive = customer.get('active');
const newsletterSubscribed = customer.get('newsletter_subscribed');Updating Customer Data 
// Update customer information
const customer = await new Nimbu.Query('customers').get(customerId);
customer.set('phone', '+32 123 456 789');
customer.set('loyalty_points', 100);
customer.set('newsletter_subscribed', true);
await customer.save();Customer Roles 
// Add customer to a role
const customer = await new Nimbu.Query('customers').get(customerId);
const role = await new Nimbu.Query('roles').get(roleId);
role.getCustomers().add(customer);
await role.save();
// Check if customer has a role
const customerRoles = customer.relation('roles');
const roles = await customerRoles.query().collection().fetch();
console.log(`Customer has ${roles.length} roles`);Customer Orders 
// Get customer's orders
const customer = await new Nimbu.Query('customers').get(customerId);
const orders = customer.relation('orders');
const customerOrders = await orders.query()
  .descending('created_at')
  .limit(10)
  .collection()
  .fetch();
console.log(`Found ${customerOrders.length} orders`);Nimbu.Collection 
The Collection class is a utility for working with custom content types (channels) in Nimbu.
Querying Collections 
// Query a custom collection
const Collection = Nimbu.Object.extend('blog_posts');
const query = new Nimbu.Query(Collection);
query.equalTo('published', true);
query.descending('published_at');
query.limit(10);
const posts = await query.collection().fetch();Creating Collection Entries 
// Create a new blog post
const BlogPost = Nimbu.Object.extend('blog_posts');
const post = new BlogPost();
post.set('title', 'My First Blog Post');
post.set('content', 'This is the content of my post');
post.set('published', true);
post.set('published_at', new Date());
await post.save();Collection with Custom Fields 
// Work with custom fields
const entry = new Nimbu.Object('projects');
entry.set('project_name', 'New Website');
entry.set('project_status', 'in_progress');
entry.set('project_deadline', new Date('2024-12-31'));
entry.set('project_tags', ['web', 'design', 'development']);
await entry.save();Nimbu.Product 
The Product class provides specialized methods for e-commerce products.
Product Queries 
// Find active products
const query = new Nimbu.Query('products');
query.equalTo('active', true);
query.greaterThan('stock', 0);
query.ascending('name');
const products = await query.collection().fetch();Product Properties 
const product = await new Nimbu.Query('products').get(productId);
// Basic properties
const name = product.get('name');
const price = product.get('price');
const stock = product.get('stock');
const sku = product.get('sku');
// Product attributes
const description = product.get('description');
const category = product.get('category');
const tags = product.get('tags');
const active = product.get('active');Product Variants 
// Access product variants
const product = await new Nimbu.Query('products').get(productId);
const variants = product.get('variants');
if (variants && variants.length > 0) {
  variants.forEach(variant => {
    console.log(`${variant.name}: €${variant.price}`);
  });
}Product Stock Management 
// Update product stock
const product = await new Nimbu.Query('products').get(productId);
// Decrement stock (atomic operation)
await product.increment('stock', -1);
await product.save();
// Check stock level
if (product.get('stock') === 0) {
  product.set('active', false);
  await product.save();
}Product Categories 
// Find products by category
const query = new Nimbu.Query('products');
query.equalTo('category', 'electronics');
query.equalTo('active', true);
const electronics = await query.collection().fetch();Nimbu.Order 
The Order class represents customer orders and purchases.
Order Queries 
// Find recent orders
const query = new Nimbu.Query('orders');
query.descending('created_at');
query.limit(20);
query.include('customer');
const recentOrders = await query.collection().fetch();
recentOrders.forEach(order => {
  const customer = order.get('customer');
  console.log(`Order ${order.id} - ${customer.get('name')}`);
});Order Properties 
const order = await new Nimbu.Query('orders').get(orderId);
// Order details
const orderNumber = order.get('order_number');
const status = order.get('status');
const total = order.get('total');
const currency = order.get('currency');
// Order items
const items = order.get('items');
items.forEach(item => {
  console.log(`${item.quantity}x ${item.product_name} - €${item.price}`);
});
// Customer information
const customer = order.get('customer');
const shippingAddress = order.get('shipping_address');
const billingAddress = order.get('billing_address');Order Status Management 
// Update order status
const order = await new Nimbu.Query('orders').get(orderId);
order.set('status', 'processing');
order.set('processed_at', new Date());
await order.save();Order Filtering 
// Find pending orders
const query = new Nimbu.Query('orders');
query.containedIn('status', ['pending', 'processing']);
query.include('customer');
const pendingOrders = await query.collection().fetch();
console.log(`${pendingOrders.length} orders need attention`);Nimbu.File 
The File class handles file uploads and management.
Uploading Files 
// Upload a file
const fileData = {
  name: 'document.pdf',
  data: fileBuffer, // or base64 string
  contentType: 'application/pdf'
};
const file = new Nimbu.File(fileData);
await file.save();
console.log('File uploaded:', file.url());Attaching Files to Objects 
// Attach file to an object
const document = new Nimbu.Object('documents');
document.set('title', 'Annual Report');
document.set('file', file);
await document.save();
// Access file later
const savedDocument = await new Nimbu.Query('documents').get(documentId);
const attachedFile = savedDocument.get('file');
console.log('Download URL:', attachedFile.url());File Metadata 
// Access file properties
const fileName = file.name();
const fileUrl = file.url();
const fileSize = file.get('size');
const contentType = file.get('content_type');
console.log(`File: ${fileName} (${fileSize} bytes)`);Nimbu.Gallery 
The Gallery class manages image galleries and media collections.
Creating Galleries 
// Create a new gallery
const gallery = new Nimbu.Object('galleries');
gallery.set('name', 'Product Photos');
gallery.set('description', 'Photos of our products');
await gallery.save();Adding Images to Gallery 
// Add images to gallery
const image1 = new Nimbu.File({ name: 'product1.jpg', data: imageData1 });
const image2 = new Nimbu.File({ name: 'product2.jpg', data: imageData2 });
await image1.save();
await image2.save();
const gallery = await new Nimbu.Query('galleries').get(galleryId);
const images = gallery.get('images') || [];
images.push(image1, image2);
gallery.set('images', images);
await gallery.save();Gallery Queries 
// Find galleries
const query = new Nimbu.Query('galleries');
query.equalTo('active', true);
query.descending('created_at');
const galleries = await query.collection().fetch();
galleries.forEach(gallery => {
  const images = gallery.get('images') || [];
  console.log(`${gallery.get('name')}: ${images.length} images`);
});Nimbu.Role 
The Role class manages customer roles and permissions.
Creating Roles 
// Create a new role
const role = new Nimbu.Object('roles');
role.set('name', 'Premium Members');
role.set('description', 'Customers with premium access');
await role.save();Adding Customers to Roles 
// Add customers to a role
const role = await new Nimbu.Query('roles').get(roleId);
const customer = await new Nimbu.Query('customers').get(customerId);
role.getCustomers().add(customer);
await role.save();
console.log('Customer added to role');Querying Role Members 
// Get all customers in a role
const role = await new Nimbu.Query('roles').get(roleId);
const customers = await role.getCustomers().list();
console.log(`Role has ${customers.length} members`);
customers.forEach(customer => {
  console.log(`- ${customer.get('name')} (${customer.get('email')})`);
});Removing Customers from Roles 
// Remove customer from role
const role = await new Nimbu.Query('roles').get(roleId);
const customer = await new Nimbu.Query('customers').get(customerId);
role.getCustomers().remove(customer);
await role.save();Finding Roles by Customer 
// Find all roles for a customer
const customer = await new Nimbu.Query('customers').get(customerId);
const roles = customer.relation('roles');
const customerRoles = await roles.query().collection().fetch();
customerRoles.forEach(role => {
  console.log(`Customer has role: ${role.get('name')}`);
});Role-Based Access Control 
// Check if customer has specific role
async function hasRole(customer, roleName) {
  const roles = customer.relation('roles');
  const query = roles.query();
  query.equalTo('name', roleName);
  
  const matchingRoles = await query.collection().fetch();
  return matchingRoles.length > 0;
}
// Usage
const customer = await new Nimbu.Query('customers').get(customerId);
const isPremium = await hasRole(customer, 'Premium Members');
if (isPremium) {
  console.log('Customer has premium access');
}Nimbu.API 
The API class provides direct access to the Nimbu REST API for advanced use cases.
Making API Requests 
// GET request
const response = await Nimbu.API.get('/api/products', {
  params: {
    active: true,
    limit: 10
  }
});
console.log('Products:', response.data);POST Requests 
// Create resource via API
const response = await Nimbu.API.post('/api/customers', {
  data: {
    email: '[email protected]',
    name: 'John Doe'
  }
});
console.log('Created customer:', response.data);PUT/PATCH Requests 
// Update resource
const response = await Nimbu.API.patch(`/api/products/${productId}`, {
  data: {
    price: 29.99,
    stock: 100
  }
});DELETE Requests 
// Delete resource
await Nimbu.API.delete(`/api/products/${productId}`);
console.log('Product deleted');Custom Headers 
// Add custom headers
const response = await Nimbu.API.get('/api/custom-endpoint', {
  headers: {
    'X-Custom-Header': 'value'
  }
});Common Patterns 
Customer Registration Flow 
async function registerCustomer(email, password, name) {
  // Check if email already exists
  const query = new Nimbu.Query('customers');
  query.equalTo('email', email.toLowerCase());
  const existing = await query.first();
  if (existing) {
    throw new Error('Email already registered');
  }
  // Create new customer
  const customer = new Nimbu.Customer();
  customer.set('email', email.toLowerCase());
  customer.set('password', password);
  customer.set('name', name);
  customer.set('active', true);
  await customer.save();
  // Add to default role
  const defaultRole = await new Nimbu.Query('roles')
    .equalTo('name', 'Customers')
    .first();
  if (defaultRole) {
    defaultRole.getCustomers().add(customer);
    await defaultRole.save();
  }
  return customer;
}Order Processing 
async function processOrder(orderId) {
  const order = await new Nimbu.Query('orders').get(orderId);
  // Validate stock availability
  const items = order.get('items');
  for (const item of items) {
    const product = await new Nimbu.Query('products').get(item.product_id);
    if (product.get('stock') < item.quantity) {
      throw new Error(`Insufficient stock for ${product.get('name')}`);
    }
  }
  // Update stock levels
  for (const item of items) {
    const product = await new Nimbu.Query('products').get(item.product_id);
    await product.increment('stock', -item.quantity);
    await product.save();
  }
  // Update order status
  order.set('status', 'processing');
  order.set('processed_at', new Date());
  await order.save();
  return order;
}File Upload with Validation 
async function uploadDocument(fileData, metadata) {
  // Validate file type
  const allowedTypes = ['application/pdf', 'image/jpeg', 'image/png'];
  if (!allowedTypes.includes(fileData.contentType)) {
    throw new Error('Invalid file type');
  }
  // Validate file size (5MB max)
  const maxSize = 5 * 1024 * 1024;
  if (fileData.size > maxSize) {
    throw new Error('File too large');
  }
  // Upload file
  const file = new Nimbu.File(fileData);
  await file.save();
  // Create document record
  const document = new Nimbu.Object('documents');
  document.set('title', metadata.title);
  document.set('description', metadata.description);
  document.set('file', file);
  document.set('uploaded_by', metadata.uploadedBy);
  await document.save();
  return {
    id: document.id,
    url: file.url()
  };
}Best Practices 
1. Use Specialized Classes When Available 
// Good: Use specialized class
const customer = new Nimbu.Customer();
// Avoid: Generic object for specialized types
const customer = new Nimbu.Object('customers');2. Include Relations to Avoid N+1 Queries 
// Good: Include related data
const query = new Nimbu.Query('orders');
query.include('customer');
query.include('products');
const orders = await query.collection().fetch();
// Avoid: Fetching relations separately
const orders = await query.collection().fetch();
for (const order of orders) {
  const customer = await order.get('customer').fetch(); // N+1 query!
}3. Validate Data Before Saving 
// Good: Validate before save
if (!email || !email.includes('@')) {
  throw new Error('Invalid email');
}
await customer.save();
// Avoid: Saving without validation
await customer.save(); // May fail with unclear error4. Handle Errors Gracefully 
// Good: Error handling
try {
  await order.save();
} catch (error) {
  console.error('Failed to save order:', error.message);
  // Handle error appropriately
}
// Avoid: No error handling
await order.save(); // Unhandled errorsNext Steps 
- Review Nimbu.Object - Base class fundamentals
- Review Nimbu.Query - Query patterns
- Explore Callbacks - React to data changes
- Check Cloud Functions - Use SDK in functions