BaasixBaasix

Integration Guide

← Back to Documentation Home

Table of Contents

  1. Getting Started
  2. Authentication Integration
  3. Data Management Workflows
  4. Real-time Integration
  5. File Upload Integration
  6. Multi-tenant Integration
  7. Error Handling Patterns
  8. Frontend Integration Examples
  9. Mobile App Integration
  10. Testing Your Integration

Getting Started

This guide provides practical examples and patterns for integrating with the BAASIX API. Each example includes complete code snippets that you can adapt for your application.

Initial Setup

// Base API configuration
const BAASIX_BASE_URL = 'https://your-baasix-instance.com';
const API_VERSION = 'v1'; // Optional if using versioning

// HTTP client setup (using fetch)
class BaaSixClient {
  constructor(baseUrl, token = null) {
    this.baseUrl = baseUrl;
    this.token = token;
  }

  async request(endpoint, options = {}) {
    const url = `${this.baseUrl}${endpoint}`;
    const config = {
      ...options,
      headers: {
        'Content-Type': 'application/json',
        ...(this.token && { 'Authorization': `Bearer ${this.token}` }),
        ...options.headers,
      },
    };

    try {
      const response = await fetch(url, config);

      if (!response.ok) {
        const error = await response.json().catch(() => ({
          message: response.statusText,
          code: response.status
        }));
        throw new BaaSixError(error.message, error.code, error.details);
      }

      return await response.json();
    } catch (error) {
      if (error instanceof BaaSixError) throw error;
      throw new BaaSixError('Network error', 0, { originalError: error });
    }
  }

  // Convenience methods
  get(endpoint) { return this.request(endpoint, { method: 'GET' }); }
  post(endpoint, data) { return this.request(endpoint, { method: 'POST', body: JSON.stringify(data) }); }
  patch(endpoint, data) { return this.request(endpoint, { method: 'PATCH', body: JSON.stringify(data) }); }
  delete(endpoint) { return this.request(endpoint, { method: 'DELETE' }); }
}

// Custom error class
class BaaSixError extends Error {
  constructor(message, code, details = {}) {
    super(message);
    this.name = 'BaaSixError';
    this.code = code;
    this.details = details;
  }
}

// Initialize client
const baasix = new BaaSixClient(BAASIX_BASE_URL);

Authentication Integration

Complete Authentication Flow

class AuthManager {
  constructor(client) {
    this.client = client;
    this.token = localStorage.getItem('baasix_token');
    this.user = JSON.parse(localStorage.getItem('baasix_user') || 'null');

    if (this.token) {
      this.client.token = this.token;
    }
  }

  async login(email, password) {
    try {
      const response = await this.client.post('/auth/login', {
        email,
        password
      });

      this.token = response.token;
      this.user = response.user;
      this.client.token = this.token;

      // Persist authentication
      localStorage.setItem('baasix_token', this.token);
      localStorage.setItem('baasix_user', JSON.stringify(this.user));

      return { success: true, user: this.user };
    } catch (error) {
      console.error('Login failed:', error);
      return { success: false, error: error.message };
    }
  }

  async register(userData) {
    try {
      const response = await this.client.post('/auth/register', userData);

      // Auto-login after registration
      if (response.token) {
        return await this.login(userData.email, userData.password);
      }

      return { success: true, message: 'Registration successful' };
    } catch (error) {
      return { success: false, error: error.message };
    }
  }

  async logout() {
    try {
      await this.client.get('/auth/logout');
    } catch (error) {
      console.error('Logout request failed:', error);
    } finally {
      // Clear local state regardless of API response
      this.token = null;
      this.user = null;
      this.client.token = null;
      localStorage.removeItem('baasix_token');
      localStorage.removeItem('baasix_user');
    }
  }

  async getCurrentUser() {
    if (!this.token) return null;

    try {
      const response = await this.client.get('/auth/me');
      this.user = response.data;
      localStorage.setItem('baasix_user', JSON.stringify(this.user));
      return this.user;
    } catch (error) {
      // Token might be expired
      await this.logout();
      return null;
    }
  }

  async refreshToken() {
    // If your API supports token refresh
    try {
      const response = await this.client.post('/auth/refresh');
      this.token = response.token;
      this.client.token = this.token;
      localStorage.setItem('baasix_token', this.token);
      return true;
    } catch (error) {
      await this.logout();
      return false;
    }
  }

  isAuthenticated() {
    return !!this.token;
  }
}

// Usage
const auth = new AuthManager(baasix);

// Login example
const loginForm = document.getElementById('loginForm');
loginForm.addEventListener('submit', async (e) => {
  e.preventDefault();

  const formData = new FormData(e.target);
  const result = await auth.login(
    formData.get('email'),
    formData.get('password')
  );

  if (result.success) {
    window.location.href = '/dashboard';
  } else {
    showError(result.error);
  }
});
class MagicLinkAuth {
  constructor(client) {
    this.client = client;
  }

  async requestMagicLink(email, redirectUrl = null) {
    try {
      const response = await this.client.post('/auth/magiclink', {
        email,
        redirectUrl: redirectUrl || window.location.origin + '/auth/verify'
      });

      return {
        success: true,
        message: 'Magic link sent to your email'
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  async verifyMagicLink(token) {
    try {
      const response = await this.client.get(`/auth/magiclink/${token}`);

      // Store the authentication token
      localStorage.setItem('baasix_token', response.token);
      localStorage.setItem('baasix_user', JSON.stringify(response.user));

      return {
        success: true,
        user: response.user,
        token: response.token
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
}

// Usage for magic link
const magicAuth = new MagicLinkAuth(baasix);

// Request magic link
document.getElementById('magicLinkBtn').addEventListener('click', async () => {
  const email = document.getElementById('email').value;
  const result = await magicAuth.requestMagicLink(email);

  if (result.success) {
    showMessage('Check your email for the login link');
  } else {
    showError(result.error);
  }
});

// Verify magic link (on the redirect page)
const urlParams = new URLSearchParams(window.location.search);
const token = urlParams.get('token');

if (token) {
  magicAuth.verifyMagicLink(token).then(result => {
    if (result.success) {
      window.location.href = '/dashboard';
    } else {
      showError('Invalid or expired link');
    }
  });
}

Data Management Workflows

Complete CRUD Operations

class DataManager {
  constructor(client) {
    this.client = client;
  }

  // Create with validation and error handling
  async create(collection, data, options = {}) {
    try {
      // Validate required fields (client-side)
      if (options.requiredFields) {
        const missing = options.requiredFields.filter(field => !data[field]);
        if (missing.length > 0) {
          throw new Error(`Missing required fields: ${missing.join(', ')}`);
        }
      }

      const response = await this.client.post(`/items/${collection}`, data);

      // Handle post-creation actions
      if (options.onCreate) {
        await options.onCreate(response.data);
      }

      return {
        success: true,
        data: response.data,
        id: response.data.id
      };
    } catch (error) {
      return {
        success: false,
        error: error.message,
        details: error.details
      };
    }
  }

  // Read with complex queries
  async read(collection, options = {}) {
    try {
      const queryParams = new URLSearchParams();

      // Build query parameters
      if (options.fields) {
        queryParams.append('fields', Array.isArray(options.fields)
          ? options.fields.join(',')
          : options.fields
        );
      }

      if (options.filter) {
        queryParams.append('filter', JSON.stringify(options.filter));
      }

      if (options.sort) {
        queryParams.append('sort', JSON.stringify(options.sort));
      }

      if (options.limit) {
        queryParams.append('limit', options.limit);
      }

      if (options.page) {
        queryParams.append('page', options.page);
      }

      if (options.search) {
        queryParams.append('search', options.search);
        if (options.searchFields) {
          queryParams.append('searchFields', options.searchFields.join(','));
        }
      }

      const url = `/items/${collection}?${queryParams.toString()}`;
      const response = await this.client.get(url);

      return {
        success: true,
        data: response.data,
        totalCount: response.totalCount,
        page: options.page || 1,
        limit: options.limit || response.data.length
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  // Update with optimistic locking
  async update(collection, id, data, options = {}) {
    try {
      // Optimistic locking check
      if (options.version && data.version !== options.version) {
        throw new Error('Record has been modified by another user');
      }

      const response = await this.client.patch(`/items/${collection}/${id}`, data);

      if (options.onUpdate) {
        await options.onUpdate(response.data);
      }

      return {
        success: true,
        data: response.data
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  // Delete with confirmation
  async delete(collection, id, options = {}) {
    try {
      if (options.confirm && !confirm('Are you sure you want to delete this item?')) {
        return { success: false, cancelled: true };
      }

      await this.client.delete(`/items/${collection}/${id}`);

      if (options.onDelete) {
        await options.onDelete(id);
      }

      return { success: true };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  // Batch operations
  async batchCreate(collection, items) {
    const results = [];
    for (const item of items) {
      const result = await this.create(collection, item);
      results.push(result);

      // Stop on first error (optional)
      if (!result.success) {
        return {
          success: false,
          results,
          error: `Batch create failed at item ${results.length}`
        };
      }
    }

    return {
      success: true,
      results,
      created: results.length
    };
  }
}

// Usage examples
const dataManager = new DataManager(baasix);

// Create a blog post
const createPost = async () => {
  const result = await dataManager.create('posts', {
    title: 'My First Post',
    content: 'This is the content of my first post.',
    status: 'draft'
  }, {
    requiredFields: ['title', 'content'],
    onCreate: (post) => {
      console.log('Post created:', post);
      // Redirect to edit page
      window.location.href = `/posts/${post.id}/edit`;
    }
  });

  if (!result.success) {
    showError(result.error);
  }
};

// Read posts with pagination and search
const loadPosts = async (page = 1, search = '') => {
  const result = await dataManager.read('posts', {
    fields: ['id', 'title', 'excerpt', 'createdAt', 'author.name'],
    filter: {
      status: 'published'
    },
    sort: {
      createdAt: 'desc'
    },
    limit: 10,
    page,
    search,
    searchFields: ['title', 'content']
  });

  if (result.success) {
    displayPosts(result.data);
    updatePagination(result.page, result.totalCount, result.limit);
  } else {
    showError(result.error);
  }
};

Advanced Query Patterns

class AdvancedQueries {
  constructor(client) {
    this.client = client;
  }

  // Dashboard analytics query
  async getDashboardStats(dateRange = 30) {
    const endDate = new Date();
    const startDate = new Date();
    startDate.setDate(startDate.getDate() - dateRange);

    try {
      const [postsStats, usersStats, commentsStats] = await Promise.all([
        // Posts analytics
        this.client.post('/items/posts', {
          filter: {
            createdAt: {
              gte: startDate.toISOString(),
              lte: endDate.toISOString()
            }
          },
          groupBy: ['date:day:createdAt'],
          aggregate: {
            totalPosts: { function: 'count', field: 'id' },
            totalViews: { function: 'sum', field: 'views' }
          }
        }),

        // User registration stats
        this.client.post('/items/baasix_User', {
          filter: {
            createdAt: {
              gte: startDate.toISOString(),
              lte: endDate.toISOString()
            }
          },
          groupBy: ['date:day:createdAt'],
          aggregate: {
            newUsers: { function: 'count', field: 'id' }
          }
        }),

        // Comments activity
        this.client.post('/items/comments', {
          filter: {
            createdAt: {
              gte: startDate.toISOString(),
              lte: endDate.toISOString()
            }
          },
          aggregate: {
            totalComments: { function: 'count', field: 'id' },
            avgCommentsPerPost: { function: 'avg', field: 'post_id' }
          }
        })
      ]);

      return {
        posts: postsStats.data,
        users: usersStats.data,
        comments: commentsStats.data
      };
    } catch (error) {
      throw new Error(`Failed to load dashboard stats: ${error.message}`);
    }
  }

  // Complex relational query
  async getPostsWithFullData(filters = {}) {
    try {
      const response = await this.client.post('/items/posts', {
        fields: [
          'id', 'title', 'excerpt', 'content', 'status', 'createdAt',
          'author.name', 'author.email', 'author.profile.avatar',
          'category.name', 'category.slug',
          'tags.name', 'tags.color',
          'comments.content', 'comments.createdAt', 'comments.user.name'
        ],
        filter: {
          AND: [
            { status: 'published' },
            { 'author.status': 'active' },
            { 'comments.approved': true },
            ...Object.entries(filters).map(([key, value]) => ({ [key]: value }))
          ]
        },
        sort: {
          createdAt: 'desc'
        },
        limit: 20
      });

      return response.data.map(post => ({
        ...post,
        // Calculate derived properties
        commentCount: post.comments ? post.comments.length : 0,
        hasComments: post.comments && post.comments.length > 0,
        tagNames: post.tags ? post.tags.map(tag => tag.name) : []
      }));
    } catch (error) {
      throw new Error(`Failed to load posts: ${error.message}`);
    }
  }

  // Geospatial query example
  async findNearbyStores(lat, lng, radius = 5000) {
    try {
      const response = await this.client.post('/items/stores', {
        fields: [
          'id', 'name', 'address', 'phone',
          '$ST_Distance(location, ST_MakePoint(' + lng + ', ' + lat + '))$ as distance'
        ],
        filter: {
          AND: [
            { status: 'active' },
            {
              location: {
                dwithin: {
                  point: [lng, lat],
                  distance: radius
                }
              }
            }
          ]
        },
        sort: {
          distance: 'asc'
        },
        limit: 10
      });

      return response.data;
    } catch (error) {
      throw new Error(`Failed to find nearby stores: ${error.message}`);
    }
  }
}

Real-time Integration

Socket.IO Client Integration

See also:

class RealtimeManager {
  constructor(baseUrl, token) {
    this.baseUrl = baseUrl;
    this.token = token;
    this.socket = null;
    this.eventHandlers = new Map();
  }

  async connect() {
    // Import socket.io-client (or include via CDN)
    const io = await import('socket.io-client');

    this.socket = io.connect(this.baseUrl, {
      path: '/realtime',
      auth: {
        token: this.token
      },
      transports: ['websocket', 'polling']
    });

    this.setupEventHandlers();
    return new Promise((resolve, reject) => {
      this.socket.on('connect', () => {
        console.log('Connected to BAASIX realtime server');
        resolve();
      });

      this.socket.on('connect_error', (error) => {
        console.error('Connection failed:', error);
        reject(error);
      });
    });
  }

  setupEventHandlers() {
    // Handle item events
    this.socket.on('item.created', (data) => {
      this.handleEvent('item.created', data);
    });

    this.socket.on('item.updated', (data) => {
      this.handleEvent('item.updated', data);
    });

    this.socket.on('item.deleted', (data) => {
      this.handleEvent('item.deleted', data);
    });

    // Handle notification events
    this.socket.on('notification.new', (data) => {
      this.handleEvent('notification.new', data);
      this.showNotification(data.notification);
    });

    // Handle user events
    this.socket.on('user.connected', (data) => {
      this.handleEvent('user.connected', data);
    });

    this.socket.on('user.disconnected', (data) => {
      this.handleEvent('user.disconnected', data);
    });
  }

  handleEvent(eventType, data) {
    const handlers = this.eventHandlers.get(eventType) || [];
    handlers.forEach(handler => {
      try {
        handler(data);
      } catch (error) {
        console.error(`Error in ${eventType} handler:`, error);
      }
    });
  }

  on(eventType, handler) {
    if (!this.eventHandlers.has(eventType)) {
      this.eventHandlers.set(eventType, []);
    }
    this.eventHandlers.get(eventType).push(handler);
  }

  off(eventType, handler) {
    const handlers = this.eventHandlers.get(eventType);
    if (handlers) {
      const index = handlers.indexOf(handler);
      if (index > -1) {
        handlers.splice(index, 1);
      }
    }
  }

  showNotification(notification) {
    // Create notification UI
    const notifElement = document.createElement('div');
    notifElement.className = 'notification';
    notifElement.innerHTML = `
      <h4>${notification.title}</h4>
      <p>${notification.message}</p>
      <button onclick="this.parentElement.remove()">×</button>
    `;

    document.getElementById('notifications-container').appendChild(notifElement);

    // Auto-remove after 5 seconds
    setTimeout(() => {
      if (notifElement.parentElement) {
        notifElement.remove();
      }
    }, 5000);
  }

  disconnect() {
    if (this.socket) {
      this.socket.disconnect();
      this.socket = null;
    }
  }
}

// Usage
const realtime = new RealtimeManager(BAASIX_BASE_URL, auth.token);

// Connect when user logs in
auth.login(email, password).then(result => {
  if (result.success) {
    realtime.connect();
  }
});

// Listen for specific events
realtime.on('item.created', (data) => {
  if (data.collection === 'posts') {
    // Refresh posts list
    loadPosts();
    showMessage(`New post created: ${data.item.title}`);
  }
});

realtime.on('notification.new', (data) => {
  // Update notification count
  updateNotificationBadge();
});

// Live data updates for specific collections
class LiveDataTable {
  constructor(collection, container, realtime) {
    this.collection = collection;
    this.container = container;
    this.realtime = realtime;
    this.data = [];

    this.setupRealtimeListeners();
  }

  setupRealtimeListeners() {
    this.realtime.on('item.created', (data) => {
      if (data.collection === this.collection) {
        this.data.unshift(data.item);
        this.render();
      }
    });

    this.realtime.on('item.updated', (data) => {
      if (data.collection === this.collection) {
        const index = this.data.findIndex(item => item.id === data.item.id);
        if (index > -1) {
          this.data[index] = { ...this.data[index], ...data.item };
          this.render();
        }
      }
    });

    this.realtime.on('item.deleted', (data) => {
      if (data.collection === this.collection) {
        this.data = this.data.filter(item => item.id !== data.itemId);
        this.render();
      }
    });
  }

  async load() {
    const result = await dataManager.read(this.collection);
    if (result.success) {
      this.data = result.data;
      this.render();
    }
  }

  render() {
    // Render table with current data
    this.container.innerHTML = `
      <table>
        <thead>
          <tr>
            <th>ID</th>
            <th>Title</th>
            <th>Status</th>
            <th>Created</th>
          </tr>
        </thead>
        <tbody>
          ${this.data.map(item => `
            <tr>
              <td>${item.id}</td>
              <td>${item.title}</td>
              <td>${item.status}</td>
              <td>${new Date(item.createdAt).toLocaleDateString()}</td>
            </tr>
          `).join('')}
        </tbody>
      </table>
    `;
  }
}

// Usage
const postsTable = new LiveDataTable(
  'posts',
  document.getElementById('posts-table'),
  realtime
);
postsTable.load();

File Upload Integration

Complete File Upload Solution

class FileManager {
  constructor(client) {
    this.client = client;
  }

  async uploadFile(file, options = {}) {
    try {
      const formData = new FormData();
      formData.append('file', file);

      if (options.folder) {
        formData.append('folder', options.folder);
      }

      if (options.title) {
        formData.append('title', options.title);
      }

      // Custom upload endpoint that handles FormData
      const response = await fetch(`${this.client.baseUrl}/files`, {
        method: 'POST',
        headers: {
          ...(this.client.token && { 'Authorization': `Bearer ${this.client.token}` }),
          // Don't set Content-Type for FormData
        },
        body: formData
      });

      if (!response.ok) {
        const error = await response.json();
        throw new Error(error.message || 'Upload failed');
      }

      const result = await response.json();

      if (options.onUpload) {
        options.onUpload(result.data);
      }

      return {
        success: true,
        file: result.data
      };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }

  async uploadWithProgress(file, options = {}) {
    return new Promise((resolve, reject) => {
      const xhr = new XMLHttpRequest();
      const formData = new FormData();
      formData.append('file', file);

      // Progress tracking
      xhr.upload.addEventListener('progress', (e) => {
        if (e.lengthComputable && options.onProgress) {
          const percentComplete = (e.loaded / e.total) * 100;
          options.onProgress(percentComplete);
        }
      });

      xhr.addEventListener('load', () => {
        if (xhr.status >= 200 && xhr.status < 300) {
          const response = JSON.parse(xhr.responseText);
          resolve({
            success: true,
            file: response.data
          });
        } else {
          reject(new Error(`Upload failed: ${xhr.statusText}`));
        }
      });

      xhr.addEventListener('error', () => {
        reject(new Error('Upload failed'));
      });

      xhr.open('POST', `${this.client.baseUrl}/files`);
      if (this.client.token) {
        xhr.setRequestHeader('Authorization', `Bearer ${this.client.token}`);
      }
      xhr.send(formData);
    });
  }

  async getFileUrl(fileId, options = {}) {
    const params = new URLSearchParams();

    if (options.width) params.append('width', options.width);
    if (options.height) params.append('height', options.height);
    if (options.quality) params.append('quality', options.quality);
    if (options.format) params.append('format', options.format);

    const query = params.toString() ? `?${params.toString()}` : '';
    return `${this.client.baseUrl}/assets/${fileId}${query}`;
  }

  async deleteFile(fileId) {
    try {
      await this.client.delete(`/files/${fileId}`);
      return { success: true };
    } catch (error) {
      return {
        success: false,
        error: error.message
      };
    }
  }
}

// File upload component
class FileUploadComponent {
  constructor(container, fileManager, options = {}) {
    this.container = container;
    this.fileManager = fileManager;
    this.options = {
      multiple: false,
      accept: '*/*',
      maxSize: 10 * 1024 * 1024, // 10MB
      ...options
    };

    this.render();
    this.setupEventListeners();
  }

  render() {
    this.container.innerHTML = `
      <div class="file-upload">
        <input type="file"
               id="fileInput"
               ${this.options.multiple ? 'multiple' : ''}
               accept="${this.options.accept}"
               style="display: none;">

        <div class="upload-area" onclick="document.getElementById('fileInput').click()">
          <div class="upload-icon">📁</div>
          <div class="upload-text">Click to upload or drag files here</div>
          <div class="upload-hint">Max size: ${this.formatFileSize(this.options.maxSize)}</div>
        </div>

        <div class="upload-progress" style="display: none;">
          <div class="progress-bar">
            <div class="progress-fill"></div>
          </div>
          <div class="progress-text">0%</div>
        </div>

        <div class="uploaded-files"></div>
      </div>
    `;
  }

  setupEventListeners() {
    const fileInput = this.container.querySelector('#fileInput');
    const uploadArea = this.container.querySelector('.upload-area');

    fileInput.addEventListener('change', (e) => {
      this.handleFiles(Array.from(e.target.files));
    });

    // Drag and drop
    uploadArea.addEventListener('dragover', (e) => {
      e.preventDefault();
      uploadArea.classList.add('drag-over');
    });

    uploadArea.addEventListener('dragleave', () => {
      uploadArea.classList.remove('drag-over');
    });

    uploadArea.addEventListener('drop', (e) => {
      e.preventDefault();
      uploadArea.classList.remove('drag-over');
      this.handleFiles(Array.from(e.dataTransfer.files));
    });
  }

  async handleFiles(files) {
    const validFiles = files.filter(file => this.validateFile(file));

    if (validFiles.length === 0) {
      this.showError('No valid files selected');
      return;
    }

    for (const file of validFiles) {
      await this.uploadFile(file);
    }
  }

  validateFile(file) {
    if (file.size > this.options.maxSize) {
      this.showError(`File ${file.name} is too large`);
      return false;
    }

    if (this.options.accept !== '*/*') {
      const acceptedTypes = this.options.accept.split(',').map(t => t.trim());
      const fileType = file.type || '';
      const fileExtension = '.' + file.name.split('.').pop();

      const isAccepted = acceptedTypes.some(type =>
        fileType.match(type.replace('*', '.*')) || type === fileExtension
      );

      if (!isAccepted) {
        this.showError(`File type ${fileExtension} not allowed`);
        return false;
      }
    }

    return true;
  }

  async uploadFile(file) {
    const progressBar = this.container.querySelector('.progress-fill');
    const progressText = this.container.querySelector('.progress-text');
    const progressContainer = this.container.querySelector('.upload-progress');

    progressContainer.style.display = 'block';

    try {
      const result = await this.fileManager.uploadWithProgress(file, {
        onProgress: (percent) => {
          progressBar.style.width = `${percent}%`;
          progressText.textContent = `${Math.round(percent)}%`;
        },
        folder: this.options.folder
      });

      if (result.success) {
        this.displayUploadedFile(result.file);
        if (this.options.onFileUploaded) {
          this.options.onFileUploaded(result.file);
        }
      } else {
        this.showError(result.error);
      }
    } catch (error) {
      this.showError(error.message);
    } finally {
      progressContainer.style.display = 'none';
      progressBar.style.width = '0%';
      progressText.textContent = '0%';
    }
  }

  displayUploadedFile(file) {
    const container = this.container.querySelector('.uploaded-files');
    const fileElement = document.createElement('div');
    fileElement.className = 'uploaded-file';
    fileElement.innerHTML = `
      <div class="file-info">
        <div class="file-name">${file.filename}</div>
        <div class="file-size">${this.formatFileSize(file.size)}</div>
      </div>
      <div class="file-actions">
        <button onclick="window.open('${this.fileManager.getFileUrl(file.id)}')">View</button>
        <button onclick="this.parentElement.parentElement.remove()">Remove</button>
      </div>
    `;
    container.appendChild(fileElement);
  }

  formatFileSize(bytes) {
    if (bytes === 0) return '0 Bytes';
    const k = 1024;
    const sizes = ['Bytes', 'KB', 'MB', 'GB'];
    const i = Math.floor(Math.log(bytes) / Math.log(k));
    return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
  }

  showError(message) {
    // Show error message to user
    console.error(message);
    alert(message); // Replace with proper error display
  }
}

// Usage
const fileManager = new FileManager(baasix);

// Create file upload component
const uploadComponent = new FileUploadComponent(
  document.getElementById('file-upload'),
  fileManager,
  {
    multiple: true,
    accept: 'image/*,.pdf,.doc,.docx',
    maxSize: 5 * 1024 * 1024, // 5MB
    folder: 'uploads',
    onFileUploaded: (file) => {
      console.log('File uploaded:', file);
      // Add to form data or update UI
    }
  }
);

Testing Your Integration

Integration Test Suite

class BaaSixTestSuite {
  constructor(client) {
    this.client = client;
    this.testResults = [];
  }

  async runAllTests() {
    console.log('Starting BAASIX integration tests...');

    try {
      await this.testAuthentication();
      await this.testCRUDOperations();
      await this.testAdvancedQueries();
      await this.testFileOperations();
      await this.testRealtime();
    } catch (error) {
      console.error('Test suite failed:', error);
    }

    this.printResults();
  }

  async testAuthentication() {
    const auth = new AuthManager(this.client);

    // Test login
    await this.test('User Login', async () => {
      const result = await auth.login('test@example.com', 'password');
      if (!result.success) throw new Error(result.error);
      return result.user;
    });

    // Test current user
    await this.test('Get Current User', async () => {
      const user = await auth.getCurrentUser();
      if (!user) throw new Error('No current user');
      return user;
    });
  }

  async testCRUDOperations() {
    const dataManager = new DataManager(this.client);

    let createdId;

    // Test create
    await this.test('Create Item', async () => {
      const result = await dataManager.create('posts', {
        title: 'Test Post',
        content: 'This is a test post'
      });
      if (!result.success) throw new Error(result.error);
      createdId = result.id;
      return result.data;
    });

    // Test read
    await this.test('Read Items', async () => {
      const result = await dataManager.read('posts', {
        limit: 10
      });
      if (!result.success) throw new Error(result.error);
      return result.data;
    });

    // Test update
    await this.test('Update Item', async () => {
      const result = await dataManager.update('posts', createdId, {
        title: 'Updated Test Post'
      });
      if (!result.success) throw new Error(result.error);
      return result.data;
    });

    // Test delete
    await this.test('Delete Item', async () => {
      const result = await dataManager.delete('posts', createdId);
      if (!result.success) throw new Error(result.error);
      return true;
    });
  }

  async test(name, testFunction) {
    console.log(`Testing: ${name}`);

    try {
      const startTime = Date.now();
      const result = await testFunction();
      const duration = Date.now() - startTime;

      this.testResults.push({
        name,
        status: 'PASS',
        duration,
        result
      });

      console.log(`✅ ${name} - ${duration}ms`);
    } catch (error) {
      this.testResults.push({
        name,
        status: 'FAIL',
        error: error.message
      });

      console.log(`❌ ${name} - ${error.message}`);
    }
  }

  printResults() {
    const passed = this.testResults.filter(r => r.status === 'PASS').length;
    const failed = this.testResults.filter(r => r.status === 'FAIL').length;

    console.log('\n=== Test Results ===');
    console.log(`Total: ${this.testResults.length}`);
    console.log(`Passed: ${passed}`);
    console.log(`Failed: ${failed}`);

    if (failed > 0) {
      console.log('\nFailed Tests:');
      this.testResults
        .filter(r => r.status === 'FAIL')
        .forEach(r => console.log(`- ${r.name}: ${r.error}`));
    }
  }
}

// Run integration tests
const testSuite = new BaaSixTestSuite(baasix);
testSuite.runAllTests();

This comprehensive integration guide provides practical, production-ready code examples that demonstrate how to properly integrate with the BAASIX API. Each section includes error handling, best practices, and real-world usage patterns that AI developers can directly adapt and use in their applications.

← Back to Documentation Home

On this page