trustcafe-api-wrapper/docs/CUSTOM_REQUESTS.md
BarnacleBoy fd1a1566e9 chore: add comprehensive API wrapper documentation
- Add docs/ directory with 6 comprehensive documentation files:
  * INDEX.md - Main navigation and documentation index
  * GETTING_STARTED.md - Setup and configuration guide
  * API_REFERENCE.md - Complete API documentation
  * WRAPPERS.md - High-level wrappers guide
  * CUSTOM_REQUESTS.md - Advanced usage patterns
  * TROUBLESHOOTING.md - Problem-solving guide

- Update README.md with enhanced project information

- Update development.md with expanded documentation context

Extends previous session's documentation work with complete
developer and user-facing documentation.

Closes previous documentation issue.
2026-04-18 02:35:56 +00:00

17 KiB

Custom Requests Guide

This guide covers making advanced API calls using the TrustCafé API wrapper's direct request functionality. This is for when jobs or wrappers don't provide the level of control you need.

Table of Contents

Overview

What is make_request?

make_request is the core method of the APIClient class that allows you to make raw API calls to TrustCafé endpoints directly. It bypasses jobs and wrappers, giving you complete control over:

  • HTTP methods (GET, POST, PUT, DELETE)
  • API endpoints
  • Request paths
  • Request bodies
  • Query parameters
  • Authentication

When to Use Custom Requests

Use make_request when:

  • No wrapper or job exists for your operation
  • You need a custom HTTP endpoint
  • Building complex queries programmatically
  • Using specialized API features
  • Need maximum flexibility

Don't use when:

  • Standard operation is covered by wrappers (too much boilerplate)
  • Simpler option exists (jobs/wrappers)
  • You're a beginner (wrappers/jobs are easier)

Basic Usage

Fundamentals

import trustcafeapiwrapper
import os

API = trustcafeapiwrapper.APIClient(
    client_id=os.getenv("CLIENT_ID"),
    client_secret=os.getenv("CLIENT_SECRET"),
    env="alpha",
    debug=True
)

API.handle_token()

The method signature

response = API.make_request(
    method="GET",
    endpoint="content",
    path="post/some-id",
    data=None,
    authenticate=True,
    query_params=None
)

Parameters Explained

Parameter Type Required Default Description
method str Yes - HTTP method: "GET", "POST", "PUT", "DELETE"
endpoint str Yes - API service: "content", "auth", "audrey", etc.
path str Yes - API path after endpoint
data dict No None Request body for POST/PUT/DELETE
authenticate bool No True Include authentication token
query_params dict No None Query parameters for GET requests

Simple GET Request

# Get a post
response = API.make_request(
    "GET",
    "content",
    "post/my-post-id"
)

print(response)

POST Request

# Create a new resource
response = API.make_request(
    "POST",
    "content",
    "post/new",
    data={
        "postText": "My custom post",
        "parent": {
            "pk": "maintrunk#maintrunk",
            "sk": "maintrunk#maintrunk"
        }
    }
)

print(response)

PUT Request

# Update an existing resource
response = API.make_request(
    "PUT",
    "content",
    "post/my-post-id",
    data={
        "pk": "my-post-id",
        "sk": "my-post-id",
        "newPostText": "Updated content"
    }
)

print(response)

DELETE Request

# Delete a resource
response = API.make_request(
    "DELETE",
    "content",
    "post/my-post-id"
)

print(response)

Authentication

By default, authentication is automatic:

# This includes the access token in the Authorization header
response = API.make_request(
    "GET",
    "content",
    "post/my-post-id",
    authenticate=True  # Default
)

Making guest requests (no authentication):

# Useful for public endpoints
response = API.make_request(
    "GET",
    "content",
    "post/public-post",
    authenticate=False  # No token needed
)

Manual token injection (advanced):

# Sometimes you may need to bypass the token management
response = API.make_request(
    "GET",
    "content",
    "post/some-path",
    headers={
        "Authorization": "Bearer your-token-manually-set"
    }
)

Common Patterns

Pagination

TrustCafé responses may include pagination for large result sets:

def get_all_posts(limit=100):
    """Fetch all posts with pagination"""

    def fetch_page(start_key=None):
        """Fetch a single page of posts"""
        params = {"Limit": limit}

        if start_key:
            params["ExclusiveStartKey"] = start_key

        response = API.make_request(
            "GET",
            "content",
            "post/listall",
            query_params=params,
            authenticate=True
        )

        return response

    all_posts = []
    start_key = None

    while True:
        page = fetch_page(start_key)

        items = page.get('Items', [])
        all_posts.extend(items)

        # Check if there are more pages
        has_more = 'LastEvaluatedKey' in page
        if not has_more or len(items) == 0:
            break

        # Set start key for next page
        start_key = page['LastEvaluatedKey']

    return all_posts


# Usage
API.handle_token()
all_posts = get_all_posts(limit=50)
print(f"Total posts: {len(all_posts)}")

Filtering with Query Parameters

# Get posts created after a specific date
response = API.make_request(
    "GET",
    "content",
    "post/listpublic",
    query_params={
        "creationDateAfter": "2024-01-01T00:00:00Z"
    },
    authenticate=True
)

# Get posts in a specific branch
response = API.make_request(
    "GET",
    "content",
    "post/ref-subwiki/music",
    query_params={
        "maxResults": 20,
        "showDeleted": False
    },
    authenticate=True
)

Building Dynamic Paths

def get_post_by_slug(slug):
    """Get a post by slugified ID"""
    response = API.make_request(
        "GET",
        "content",
        f"post/id/{slug}",
        authenticate=True
    )
    return response

# Usage
post = get_post_by_slug("my-custom-slug-123")
def get_comments_for_post(post_id):
    """Get comments for a specific post"""
    response = API.make_request(
        "GET",
        "content",
        f"comment/bypostid/{post_id}",
        authenticate=True
    )
    return response

Advanced Usage

Multiple Endpoints

TrustCafé has multiple API service endpoints:

# Content API
content_response = API.make_request(
    "GET",
    "content",
    "post/listall",
    query_params={"Limit": 10}
)

# Auth API (for tokens)
auth_response = API.make_request(
    "POST",
    "auth",
    "token",
    data={
        "client_id": "your-id",
        "client_secret": "your-secret",
        "grant_type": "client_credentials"
    },
    authenticate=False
)

# Audrey API (various services)
audrey_response = API.make_request(
    "GET",
    "audrey",
    "some/endpoint",
    authenticate=True
)

Complex Payloads

# Building complex payloads for updates
def update_post_completion(post_id, completion_status):
    """Update post with new completion status"""
    response = API.make_request(
        "PUT",
        "content",
        f"post/{post_id}",
        data={
            "pk": post_id,
            "sk": post_id,
            "postText": "Updated post with completion status",
            "completion": completion_status
        },
        authenticate=True
    )
    return response


# Usage
update_post_completion(
    "current-post-pk",
    {
        "isComplete": True,
        "completedBy": "username",
        "completedAt": "2024-01-01"
    }
)

Batch Operations

def batch_create_posts(posts_data):
    """Create multiple posts in a single batch"""

    # Build batch request
    batch_data = {
        "Post": [
            {
                "postText": post["text"],
                "parent": {
                    "pk": post["parent_pk"],
                    "sk": post["parent_sk"]
                }
            }
            for post in posts_data
        ]
    }

    response = API.make_request(
        "POST",
        "content",
        "post/batch-create",
        data=batch_data,
        authenticate=True
    )

    return response

Error Handling for Custom Requests

def safe_api_request(method, endpoint, path, data=None):
    """
    Safely execute an API request with proper error handling.
    """
    try:
        response = API.make_request(
            method,
            endpoint,
            path,
            data=data,
            authenticate=True
        )

        # Check for unexpected errors
        if 'error' in response:
            raise Exception(f"API Error: {response['error']}")

        return response

    except Exception as e:
        print(f"Request failed: {e}")
        # Handle error: retry, notify, log, etc.
        raise

Pagination

Pagination Strategy

When handling paginated results, always check for pagination metadata:

def fetch_all_content():
    """Fetch all content with pagination handling"""

    all_content = []
    next_token = None

    while True:
        params = {
            "Limit": 100
        }

        if next_token:
            params["ExclusiveStartKey"] = next_token

        try:
            response = API.make_request(
                "GET",
                "content",
                "content/list",
                query_params=params,
                authenticate=True
            )

        except Exception as e:
            print(f"Error fetching page: {e}")
            break

        # Process current page
        items = response.get('Items', [])
        all_content.extend(items)

        # Check for next page
        has_more = 'LastEvaluatedKey' in response

        if not has_more or len(items) < 100:
            print(f"Total items retrieved: {len(all_content)}")
            break

        next_token = response['LastEvaluatedKey']
        print(f"Fetching next page (token: {next_token[:10]}...)")

    return all_content

Processing Large Datasets

def process_content_in_batches(batch_size=100):
    """Process content in manageable batches"""

    batch_number = 0

    while True:
        batch_number += 1
        print(f"Processing batch {batch_number}...")

        try:
            response = API.make_request(
                "GET",
                "content",
                "content/list",
                query_params={
                    "Limit": batch_size
                },
                authenticate=True
            )

            items = response.get('Items', [])

            if not items:
                print("No more items to process")
                break

            # Process each item in the batch
            for item in items:
                # Your processing logic here
                process_item(item)

            # Check for more items
            has_more = 'LastEvaluatedKey' in response
            if not has_more:
                break

        except Exception as e:
            print(f"Error in batch {batch_number}: {e}")
            # Retry logic could go here
            break

    print(f"Completed processing {batch_number} batches")

Error Handling

Common Error Scenarios

def handle_api_request(method, endpoint, path, data=None):
    """Robust API request handler"""

    try:
        # Make request
        response = API.make_request(
            method,
            endpoint,
            path,
            data=data,
            authenticate=True
        )

        # Check for API-level errors
        if 'error' in response:
            raise APIError(
                endpoint=endpoint,
                path=path,
                api_error=response['error'],
                message=response.get('message', '')
            )

        return response

    except APIError as e:
        print(f"API Error: {e}")
        # Retry logic or fallback could go here
        raise

    except Exception as e:
        print(f"Unexpected error: {e}")
        # Log the error
        log_error(e)
        # Could retry or use fallback
        raise

Retry Logic

import time
import logging

logger = logging.getLogger(__name__)

def retry_on_failure(request_func, max_retries=3, retry_delay=1):
    """
    Retry a request function on failure.

    Args:
        request_func: Function that makes the API request
        max_retries: Maximum number of retry attempts
        retry_delay: Seconds to wait between retries

    Returns:
        The successful response
    """
    for attempt in range(max_retries):
        try:
            return request_func()

        except Exception as e:
            logger.warning(
                f"Request failed (attempt {attempt + 1}/{max_retries}): {e}"
            )

            if attempt < max_retries - 1:
                logger.info(f"Retrying in {retry_delay} seconds...")
                time.sleep(retry_delay)
            else:
                logger.error("Max retries reached. Giving up.")
                raise

    # This line should never be reached due to raise above
    raise Exception("Unexpected state in retry function")

Examples

Example 1: Complex Query

def search_posts_advanced(search_term, branch="all", limit=50):
    """
    Advanced search for posts with filtering.

    Note: This is an EXAMPLE - you'll need to adjust to match
    TrustCafé's actual search/query API structure.
    """

    # Build search query
    query_data = {
        "search": {
            "term": search_term,
            "filters": {
                "branches": [branch] if branch != "all" else None
            }
        }
    }

    response = API.make_request(
        "POST",
        "content",
        "search",
        data=query_data,
        authenticate=True
    )

    return response

Example 2: Custom Analytics Query

def get_post_engagement_metrics(post_id):
    """
    Get engagement metrics for a post.

    Note: This requires mapping to TrustCafé's actual endpoint
    and data structure for engagement metrics.
    """

    response = API.make_request(
        "GET",
        "content",
        f"post/{post_id}/analytics",
        query_params={
            "metrics": [
                "views",
                "likes",
                "comments",
                "shares"
            ],
            "timeRange": "7d"
        },
        authenticate=True
    )

    return response

Example 3: Multi-step Transaction

def create_post_and_related_content(post_text, related_items):
    """
    Create a post and related content in a transaction-like operation.
    """
    # Step 1: Create the main post
    post_response = API.make_request(
        "POST",
        "content",
        "post",
        data={
            "postText": post_text,
            "parent": {
                "pk": "maintrunk#maintrunk",
                "sk": "maintrunk#maintrunk"
            }
        },
        authenticate=True
    )

    post_id = post_response.get('pk')

    # Step 2: Create related content (if applicable)
    if related_items:
        for item in related_items:
            API.make_request(
                "POST",
                "content",
                "related-item",
                data={
                    "postId": post_id,
                    "externalId": item['id'],
                    "type": item['type'],
                    "url": item['url']
                },
                authenticate=True
            )

    return post_response

Example 4: Conditional Operations

def conditional_update_post(post_id, updates, condition=None):
    """
    Update a post only if a condition is met.

    Note: This maps to CousinDB/GSI-style conditional updates.
    Implementation depends on TrustCafé's specific API.
    """

    update_data = {
        "pk": post_id,
        "sk": post_id,
        **updates
    }

    if condition:
        update_data["condition"] = {
            "expression": condition["expression"],
            "values": condition["values"]
        }

    response = API.make_request(
        "PUT",
        "content",
        f"post/{post_id}",
        data=update_data,
        authenticate=True
    )

    return response

Summary

make_request provides maximum flexibility for making API calls, but with greater complexity comes the need for careful:

  • Error handling - Always wrap in try/except
  • Logging - Track request/response for debugging
  • Testing - Verify your custom endpoints
  • Documentation - Document any custom patterns you discover

For most use cases, wrappers and jobs provide the right balance of simplicity and control. Use make_request when standard wrappers don't fit your needs.


Next Steps