Integrating with Kantan CMS

Integrating with Kantan CMS

4/20/2025

Integrating with Kantan CMS: A Complete Guide

This guide will walk you through the process of integrating with the Kantan CMS API to collect data, manage collections, and handle form submissions.

Prerequisites

  • Project ID and API Key
  • List of required collections
  • Form integration page identified
  • Storage location (defaults to ./tmp/)

Table of Contents

  1. API Authentication
  2. Working with Collections
  3. Retrieving Records
  4. Form Integration
  5. Complete Integration Example

API Authentication

Before accessing any data from the Kantan CMS, you must validate your API key.

const validateApiKey = async (projectId, apiKey) => {
  try {
    const response = await fetch('/v1/api/api_key/validate', {
      method: 'GET',
      headers: {
        'X-Project-Id': projectId,
        'X-API-Key': apiKey
      }
    });
    
    const data = await response.json();
    
    if (data.status === 200) {
      console.log('API key is valid');
      return true;
    } else {
      console.error('API key validation failed');
      return false;
    }
  } catch (error) {
    console.error('Error validating API key:', error);
    return false;
  }
};

Working with Collections

Counting Collections

Before retrieving collections, it's useful to know how many there are:

const countCollections = async (projectId, apiKey) => {
  try {
    const response = await fetch('/v1/api/collections_count/', {
      method: 'GET',
      headers: {
        'X-Project-Id': projectId,
        'X-API-Key': apiKey
      }
    });
    
    const data = await response.json();
    return data.count;
  } catch (error) {
    console.error('Error counting collections:', error);
    return 0;
  }
};

Retrieving Collections

You'll need to retrieve all collections and then filter based on your requirements:

const getCollections = async (projectId, apiKey, requiredCollections) => {
  try {
    const count = await countCollections(projectId, apiKey);
    const pageSize = 100;
    const pages = Math.ceil(count / pageSize);
    
    let allCollections = [];
    
    for (let page = 1; page <= pages; page++) {
      const response = await fetch(`/v1/api/collections/?page_size=${pageSize}&page_num=${page}`, {
        method: 'GET',
        headers: {
          'X-Project-Id': projectId,
          'X-API-Key': apiKey
        }
      });
      
      const data = await response.json();
      allCollections = [...allCollections, ...data.collections];
    }
    
    // Filter collections based on requirements
    if (requiredCollections && requiredCollections.length > 0) {
      return allCollections.filter(collection => 
        requiredCollections.includes(collection.name)
      );
    }
    
    return allCollections;
  } catch (error) {
    console.error('Error retrieving collections:', error);
    return [];
  }
};

Retrieving Records

Counting Records

First, count the records in a collection:

const countRecords = async (projectId, apiKey, collectionId) => {
  try {
    const response = await fetch(`/v1/api/collections/${collectionId}/records_count/`, {
      method: 'GET',
      headers: {
        'X-Project-Id': projectId,
        'X-API-Key': apiKey
      }
    });
    
    const data = await response.json();
    return data.count;
  } catch (error) {
    console.error(`Error counting records for collection ${collectionId}:`, error);
    return 0;
  }
};

Retrieving Records from a Collection

const getRecords = async (projectId, apiKey, collectionId, storagePath = './tmp/') => {
  try {
    const count = await countRecords(projectId, apiKey, collectionId);
    const pageSize = 100;
    const pages = Math.ceil(count / pageSize);
    
    let allRecords = [];
    
    for (let page = 1; page <= pages; page++) {
      const response = await fetch(`/v1/api/collections/${collectionId}/records/?page_size=${pageSize}&page_num=${page}`, {
        method: 'GET',
        headers: {
          'X-Project-Id': projectId,
          'X-API-Key': apiKey
        }
      });
      
      const data = await response.json();
      allRecords = [...allRecords, ...data.records];
    }
    
    // Save records to file
    const fs = require('fs');
    const path = require('path');
    
    // Ensure directory exists
    if (!fs.existsSync(storagePath)) {
      fs.mkdirSync(storagePath, { recursive: true });
    }
    
    // Get collection details to use as filename
    const collectionResponse = await fetch(`/v1/api/collections/${collectionId}`, {
      method: 'GET',
      headers: {
        'X-Project-Id': projectId,
        'X-API-Key': apiKey
      }
    });
    
    const collectionData = await collectionResponse.json();
    const collectionName = collectionData.collection.name;
    
    // Write to file
    fs.writeFileSync(
      path.join(storagePath, `${collectionName}.json`), 
      JSON.stringify(allRecords, null, 2)
    );
    
    return allRecords;
  } catch (error) {
    console.error(`Error retrieving records for collection ${collectionId}:`, error);
    return [];
  }
};

Form Integration

Getting a Form Token

const getFormToken = async (collectionId) => {
  try {
    const response = await fetch(`/v1/api/form_token/${collectionId}`, {
      method: 'GET'
    });
    
    const data = await response.json();
    return data.token;
  } catch (error) {
    console.error('Error getting form token:', error);
    return null;
  }
};

Form Submission

Here's an example of how to integrate a form submission into your page:

// In your HTML page:
/*
<form id="myForm">
  <input type="text" name="name" placeholder="Your Name" required>
  <input type="email" name="email" placeholder="Your Email" required>
  <textarea name="message" placeholder="Your Message" required></textarea>
  <button type="submit">Submit</button>
</form>
*/

const initializeForm = async (formElement, collectionId) => {
  // Get form token
  const token = await getFormToken(collectionId);
  
  if (!token) {
    console.error('Failed to get form token');
    return;
  }
  
  formElement.addEventListener('submit', async (event) => {
    event.preventDefault();
    
    // Collect form data
    const formData = new FormData(formElement);
    const data = {};
    
    for (let [key, value] of formData.entries()) {
      data[key] = value;
    }
    
    try {
      const response = await fetch(`/v1/api/form/${collectionId}`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          token,
          data
        })
      });
      
      if (response.ok) {
        alert('Form submitted successfully!');
        formElement.reset();
      } else {
        alert('Failed to submit form. Please try again.');
      }
    } catch (error) {
      console.error('Error submitting form:', error);
      alert('An error occurred. Please try again later.');
    }
  });
};

// Initialize the form
document.addEventListener('DOMContentLoaded', () => {
  const form = document.getElementById('myForm');
  const collectionId = 'your-form-collection-id'; // Replace with actual ID
  
  if (form) {
    initializeForm(form, collectionId);
  }
});

Complete Integration Example

Here's a complete example that puts everything together:

const KantanCMS = {
  projectId: 'your-project-id', // Replace with actual ID
  apiKey: 'your-api-key', // Replace with actual key
  requiredCollections: ['blog', 'products'], // Replace with your required collections
  formPageId: 'contact', // Replace with your form page ID
  storagePath: './tmp/', // Default storage path
  
  // Initialize the integration
  async initialize() {
    // Validate API key
    const isValid = await this.validateApiKey();
    
    if (!isValid) {
      console.error('Failed to authenticate with Kantan CMS');
      return false;
    }
    
    // Get and process collections
    const collections = await this.getCollections();
    
    if (collections.length === 0) {
      console.warn('No collections found or matching requirements');
    } else {
      console.log(`Found ${collections.length} collections`);
      
      // Process each collection
      for (const collection of collections) {
        await this.getRecords(collection.id);
      }
    }
    
    // Initialize form if on the correct page
    if (window.location.pathname.includes(this.formPageId)) {
      const form = document.getElementById('myForm');
      
      if (form) {
        // Find the form collection
        const formCollection = collections.find(c => c.type === 'form');
        
        if (formCollection) {
          await this.initializeForm(form, formCollection.id);
        } else {
          console.error('No form collection found');
        }
      }
    }
    
    return true;
  },
  
  // API validation method
  async validateApiKey() {
    try {
      const response = await fetch('/v1/api/api_key/validate', {
        method: 'GET',
        headers: {
          'X-Project-Id': this.projectId,
          'X-API-Key': this.apiKey
        }
      });
      
      const data = await response.json();
      return data.status === 200;
    } catch (error) {
      console.error('Error validating API key:', error);
      return false;
    }
  },
  
  // Count collections method
  async countCollections() {
    try {
      const response = await fetch('/v1/api/collections_count/', {
        method: 'GET',
        headers: {
          'X-Project-Id': this.projectId,
          'X-API-Key': this.apiKey
        }
      });
      
      const data = await response.json();
      return data.count;
    } catch (error) {
      console.error('Error counting collections:', error);
      return 0;
    }
  },
  
  // Get collections method
  async getCollections() {
    try {
      const count = await this.countCollections();
      const pageSize = 100;
      const pages = Math.ceil(count / pageSize);
      
      let allCollections = [];
      
      for (let page = 1; page <= pages; page++) {
        const response = await fetch(`/v1/api/collections/?page_size=${pageSize}&page_num=${page}`, {
          method: 'GET',
          headers: {
            'X-Project-Id': this.projectId,
            'X-API-Key': this.apiKey
          }
        });
        
        const data = await response.json();
        allCollections = [...allCollections, ...data.collections];
      }
      
      // Filter collections based on requirements
      if (this.requiredCollections && this.requiredCollections.length > 0) {
        return allCollections.filter(collection => 
          this.requiredCollections.includes(collection.name)
        );
      }
      
      return allCollections;
    } catch (error) {
      console.error('Error retrieving collections:', error);
      return [];
    }
  },
  
  // Count records method
  async countRecords(collectionId) {
    try {
      const response = await fetch(`/v1/api/collections/${collectionId}/records_count/`, {
        method: 'GET',
        headers: {
          'X-Project-Id': this.projectId,
          'X-API-Key': this.apiKey
        }
      });
      
      const data = await response.json();
      return data.count;
    } catch (error) {
      console.error(`Error counting records for collection ${collectionId}:`, error);
      return 0;
    }
  },
  
  // Get records method
  async getRecords(collectionId) {
    try {
      const count = await this.countRecords(collectionId);
      const pageSize = 100;
      const pages = Math.ceil(count / pageSize);
      
      let allRecords = [];
      
      for (let page = 1; page <= pages; page++) {
        const response = await fetch(`/v1/api/collections/${collectionId}/records/?page_size=${pageSize}&page_num=${page}`, {
          method: 'GET',
          headers: {
            'X-Project-Id': this.projectId,
            'X-API-Key': this.apiKey
          }
        });
        
        const data = await response.json();
        allRecords = [...allRecords, ...data.records];
      }
      
      // Save records to file (server-side only)
      if (typeof window === 'undefined') {
        const fs = require('fs');
        const path = require('path');
        
        // Ensure directory exists
        if (!fs.existsSync(this.storagePath)) {
          fs.mkdirSync(this.storagePath, { recursive: true });
        }
        
        // Get collection details to use as filename
        const collectionResponse = await fetch(`/v1/api/collections/${collectionId}`, {
          method: 'GET',
          headers: {
            'X-Project-Id': this.projectId,
            'X-API-Key': this.apiKey
          }
        });
        
        const collectionData = await collectionResponse.json();
        const collectionName = collectionData.collection.name;
        
        // Write to file
        fs.writeFileSync(
          path.join(this.storagePath, `${collectionName}.json`), 
          JSON.stringify(allRecords, null, 2)
        );
      }
      
      return allRecords;
    } catch (error) {
      console.error(`Error retrieving records for collection ${collectionId}:`, error);
      return [];
    }
  },
  
  // Get form token method
  async getFormToken(collectionId) {
    try {
      const response = await fetch(`/v1/api/form_token/${collectionId}`, {
        method: 'GET'
      });
      
      const data = await response.json();
      return data.token;
    } catch (error) {
      console.error('Error getting form token:', error);
      return null;
    }
  },
  
  // Initialize form method
  async initializeForm(formElement, collectionId) {
    // Get form token
    const token = await this.getFormToken(collectionId);
    
    if (!token) {
      console.error('Failed to get form token');
      return;
    }
    
    formElement.addEventListener('submit', async (event) => {
      event.preventDefault();
      
      // Collect form data
      const formData = new FormData(formElement);
      const data = {};
      
      for (let [key, value] of formData.entries()) {
        data[key] = value;
      }
      
      try {
        const response = await fetch(`/v1/api/form/${collectionId}`, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            token,
            data
          })
        });
        
        if (response.ok) {
          alert('Form submitted successfully!');
          formElement.reset();
        } else {
          alert('Failed to submit form. Please try again.');
        }
      } catch (error) {
        console.error('Error submitting form:', error);
        alert('An error occurred. Please try again later.');
      }
    });
  }
};

// Initialize the integration
document.addEventListener('DOMContentLoaded', async () => {
  await KantanCMS.initialize();
});

Node.js Server Example

If you're integrating with a Node.js backend, here's an example:

const fetch = require('node-fetch');
const fs = require('fs');
const path = require('path');

class KantanCMSIntegration {
  constructor(config) {
    this.projectId = config.projectId;
    this.apiKey = config.apiKey;
    this.requiredCollections = config.requiredCollections || [];
    this.storagePath = config.storagePath || './tmp/';
    
    // Create storage directory if it doesn't exist
    if (!fs.existsSync(this.storagePath)) {
      fs.mkdirSync(this.storagePath, { recursive: true });
    }
  }
  
  async init() {
    // Validate API key
    const isValid = await this.validateApiKey();
    
    if (!isValid) {
      console.error('Failed to authenticate with Kantan CMS');
      return false;
    }
    
    // Get and process collections
    const collections = await this.getCollections();
    
    if (collections.length === 0) {
      console.warn('No collections found or matching requirements');
    } else {
      console.log(`Found ${collections.length} collections`);
      
      // Process each collection
      for (const collection of collections) {
        const records = await this.getRecords(collection.id);
        console.log(`Retrieved ${records.length} records from collection ${collection.name}`);
      }
    }
    
    return true;
  }
  
  // ... include all methods from the browser example above ...
}

// Usage
const config = {
  projectId: 'your-project-id',
  apiKey: 'your-api-key',
  requiredCollections: ['blog', 'products'],
  storagePath: './data/'
};

const kantanCMS = new KantanCMSIntegration(config);
kantanCMS.init()
  .then(() => {
    console.log('Kantan CMS integration completed successfully');
  })
  .catch(error => {
    console.error('Failed to complete Kantan CMS integration:', error);
  });

Bash Commands for API Interaction

If you prefer using the command line or need to script API interactions, here are bash commands using curl for key operations:

Validating the API Key

#!/bin/bash
PROJECT_ID="your-project-id"
API_KEY="your-api-key"
STORAGE_PATH="./tmp"

# Create storage directory if it doesn't exist
mkdir -p "$STORAGE_PATH"

# Validate API key
validate_response=$(curl -s -X GET "/v1/api/api_key/validate" \
  -H "X-Project-Id: $PROJECT_ID" \
  -H "X-API-Key: $API_KEY")

if [[ "$validate_response" == *"\"status\":200"* ]]; then
  echo "API key validation successful"
else
  echo "API key validation failed"
  exit 1
fi

Get All Collections and Records

#!/bin/bash
PROJECT_ID="your-project-id"
API_KEY="your-api-key"
STORAGE_PATH="./tmp"
REQUIRED_COLLECTIONS=("blog" "products") # Add your required collections

# Get collection count
collection_count_response=$(curl -s -X GET "/v1/api/collections_count/" \
  -H "X-Project-Id: $PROJECT_ID" \
  -H "X-API-Key: $API_KEY")

collection_count=$(echo "$collection_count_response" | grep -o '"count":[0-9]*' | cut -d ":" -f2)
echo "Total collections: $collection_count"

# Calculate pages needed (100 per page)
page_size=100
pages=$(( (collection_count + page_size - 1) / page_size ))

# Get all collections
all_collections=()
for ((page=1; page<=pages; page++)); do
  echo "Fetching collections page $page of $pages..."
  collections_response=$(curl -s -X GET "/v1/api/collections/?page_size=$page_size&page_num=$page" \
    -H "X-Project-Id: $PROJECT_ID" \
    -H "X-API-Key: $API_KEY")
  
  # Save to file for processing
  echo "$collections_response" > "$STORAGE_PATH/collections_page_$page.json"
  
  # Extract collection IDs and names (requires jq)
  if command -v jq >/dev/null 2>&1; then
    collection_data=$(jq -r '.collections[] | "\(.id)|\(.name)"' "$STORAGE_PATH/collections_page_$page.json")
    while IFS= read -r line; do
      all_collections+=("$line")
    done <<< "$collection_data"
  fi
done

# Process each collection
for collection in "${all_collections[@]}"; do
  IFS='|' read -r id name <<< "$collection"
  
  # Check if this collection is in the required list
  if [[ " ${REQUIRED_COLLECTIONS[*]} " == *" $name "* ]]; then
    echo "Processing required collection: $name ($id)"
    
    # Get record count for this collection
    record_count_response=$(curl -s -X GET "/v1/api/collections/$id/records_count/" \
      -H "X-Project-Id: $PROJECT_ID" \
      -H "X-API-Key: $API_KEY")
    
    record_count=$(echo "$record_count_response" | grep -o '"count":[0-9]*' | cut -d ":" -f2)
    echo "Collection $name has $record_count records"
    
    # Calculate pages needed for records (100 per page)
    record_pages=$(( (record_count + page_size - 1) / page_size ))
    
    # Create a file to store all records
    echo "[]" > "$STORAGE_PATH/${name}_records.json"
    
    # Get all records
    for ((rpage=1; rpage<=record_pages; rpage++)); do
      echo "Fetching records page $rpage of $record_pages for $name..."
      records_response=$(curl -s -X GET "/v1/api/collections/$id/records/?page_size=$page_size&page_num=$rpage" \
        -H "X-Project-Id: $PROJECT_ID" \
        -H "X-API-Key: $API_KEY")
      
      # Save to temporary file
      echo "$records_response" > "$STORAGE_PATH/${name}_records_page_$rpage.json"
      
      # Merge with main records file (requires jq)
      if command -v jq >/dev/null 2>&1; then
        jq -s '.[0] + .[1].records' "$STORAGE_PATH/${name}_records.json" "$STORAGE_PATH/${name}_records_page_$rpage.json" > "$STORAGE_PATH/${name}_records_temp.json"
        mv "$STORAGE_PATH/${name}_records_temp.json" "$STORAGE_PATH/${name}_records.json"
      fi
    done
    
    echo "Completed processing collection: $name"
  fi
done

echo "All required collections processed and saved to $STORAGE_PATH"

Form Token and Submission Example

#!/bin/bash
COLLECTION_ID="your-form-collection-id"

# Get form token
token_response=$(curl -s -X GET "/v1/api/form_token/$COLLECTION_ID")
token=$(echo "$token_response" | grep -o '"token":"[^"]*"' | cut -d ":" -f2 | tr -d '"')

echo "Form token: $token"

# Submit form data
curl -X POST "/v1/api/form/$COLLECTION_ID" \
  -H "Content-Type: application/json" \
  -d '{
    "token": "'"$token"'",
    "data": {
      "name": "John Doe",
      "email": "john.doe@example.com",
      "message": "This is a test message"
    }
  }'

These bash scripts provide a command-line alternative for interacting with the Kantan CMS API. They use curl for API requests and jq for JSON processing (which should be installed separately if you want to process the JSON responses).

For a simplified all-in-one script that handles the entire process:

#!/bin/bash

# Configuration
PROJECT_ID="your-project-id"
API_KEY="your-api-key"
STORAGE_PATH="./tmp"
REQUIRED_COLLECTIONS=("blog" "products") # Customize as needed

# Create storage directory
mkdir -p "$STORAGE_PATH"

# Validate API key
echo "Validating API key..."
validate_response=$(curl -s -X GET "/v1/api/api_key/validate" \
  -H "X-Project-Id: $PROJECT_ID" \
  -H "X-API-Key: $API_KEY")

if [[ "$validate_response" != *"\"status\":200"* ]]; then
  echo "API key validation failed. Exiting."
  exit 1
fi

echo "API key validated successfully."

# Function to download all records from a collection
download_collection_records() {
  local collection_id=$1
  local collection_name=$2
  
  echo "Processing collection: $collection_name ($collection_id)"
  
  # Get record count
  local count_response=$(curl -s -X GET "/v1/api/collections/$collection_id/records_count/" \
    -H "X-Project-Id: $PROJECT_ID" \
    -H "X-API-Key: $API_KEY")
  
  local record_count=$(echo "$count_response" | grep -o '"count":[0-9]*' | cut -d ":" -f2)
  echo "Found $record_count records"
  
  # Create empty file for records
  echo "[]" > "$STORAGE_PATH/${collection_name}.json"
  
  # Calculate number of pages (100 per page)
  local page_size=100
  local pages=$(( (record_count + page_size - 1) / page_size ))
  
  # Download all pages
  for ((page=1; page<=pages; page++)); do
    echo "Downloading page $page of $pages..."
    
    local records_response=$(curl -s -X GET "/v1/api/collections/$collection_id/records/?page_size=$page_size&page_num=$page" \
      -H "X-Project-Id: $PROJECT_ID" \
      -H "X-API-Key: $API_KEY")
    
    # Save to temporary file
    echo "$records_response" > "$STORAGE_PATH/${collection_name}_page_$page.json"
    
    # If jq is available, merge with main file
    if command -v jq >/dev/null 2>&1; then
      jq -s '.[0] + .[1].records' "$STORAGE_PATH/${collection_name}.json" "$STORAGE_PATH/${collection_name}_page_$page.json" > "$STORAGE_PATH/${collection_name}_temp.json"
      mv "$STORAGE_PATH/${collection_name}_temp.json" "$STORAGE_PATH/${collection_name}.json"
    else
      # Append to log if jq not available
      echo "Downloaded page $page to ${collection_name}_page_$page.json" >> "$STORAGE_PATH/download_log.txt"
    fi
  done
  
  echo "Completed downloading all records for $collection_name"
}

# Get all collections
echo "Retrieving collections..."
collections_response=$(curl -s -X GET "/v1/api/collections/?page_size=100" \
  -H "X-Project-Id: $PROJECT_ID" \
  -H "X-API-Key: $API_KEY")

# Save collections to file
echo "$collections_response" > "$STORAGE_PATH/all_collections.json"

# Process each required collection
echo "Processing required collections..."
for collection_name in "${REQUIRED_COLLECTIONS[@]}"; do
  # Extract collection ID (basic extraction without jq)
  collection_id=$(echo "$collections_response" | grep -o "\"id\":\"[^\"]*\"" | grep -B1 "\"name\":\"$collection_name\"" | head -1 | cut -d ":" -f2 | tr -d '"')
  
  if [[ -n "$collection_id" ]]; then
    download_collection_records "$collection_id" "$collection_name"
  else
    echo "Collection '$collection_name' not found!"
  fi
done

echo "Process completed. Data saved to $STORAGE_PATH"

Conclusion

This guide has shown you how to integrate with the Kantan CMS API to:

  1. Validate your API credentials
  2. Count and retrieve collections
  3. Filter collections based on requirements
  4. Count and retrieve records from collections
  5. Store records locally
  6. Integrate forms into your pages
  7. Use command-line tools for scripting API interactions

The provided code examples should be adapted to your specific project needs. Remember to replace placeholder values with your actual Project ID, API Key, and other configuration details.