Skip to content

API Documentation

While U.WIKI is a static site generator, you can integrate it with external APIs or add dynamic functionality through JavaScript.

Integration Options

1. Client-Side API Calls

Add dynamic content using JavaScript:

// docs/assets/javascripts/api.js
async function fetchLatestNews() {
    const response = await fetch('https://api.example.com/news');
    const news = await response.json();
    
    document.getElementById('latest-news').innerHTML = news
        .map(item => `<li>${item.title}</li>`)
        .join('');
}

// Call on page load
document.addEventListener('DOMContentLoaded', fetchLatestNews);

2. Build-Time Data Fetching

Fetch data during build using MkDocs hooks:

# mkdocs_hooks.py
import requests
import json

def on_pre_build(config):
    # Fetch data from API
    response = requests.get('https://api.example.com/data')
    data = response.json()
    
    # Save to file that can be included
    with open('docs/includes/api-data.json', 'w') as f:
        json.dump(data, f)

3. Search API

The built-in search can be extended:

// Custom search handler
window.addEventListener('load', function() {
    const searchInput = document.querySelector('.md-search__input');
    
    searchInput.addEventListener('input', async (e) => {
        if (e.target.value.length > 2) {
            // Call external search API
            const results = await searchCustomAPI(e.target.value);
            // Display results
        }
    });
});

Common Integrations

GitHub API

Display repository information:

async function showRepoStats() {
    const response = await fetch('https://api.github.com/repos/yourusername/U.WIKI');
    const data = await response.json();
    
    document.getElementById('stars').textContent = data.stargazers_count;
    document.getElementById('forks').textContent = data.forks_count;
}

Analytics API

Track custom events:

// Track PDF downloads
document.querySelectorAll('a[href$=".pdf"]').forEach(link => {
    link.addEventListener('click', () => {
        gtag('event', 'download', {
            'event_category': 'PDF',
            'event_label': link.href
        });
    });
});

Comments System

Integrate Giscus (GitHub Discussions):

<!-- In your template -->
<script src="https://giscus.app/client.js"
        data-repo="yourusername/U.WIKI"
        data-repo-id="YOUR_REPO_ID"
        data-category="Comments"
        data-category-id="YOUR_CATEGORY_ID"
        data-mapping="pathname"
        data-strict="0"
        data-reactions-enabled="1"
        data-emit-metadata="0"
        data-input-position="top"
        data-theme="preferred_color_scheme"
        data-lang="en"
        crossorigin="anonymous"
        async>
</script>

Webhook Integration

Automatic Rebuilds

Set up webhooks to rebuild your wiki:

# webhook_server.py
from flask import Flask, request
import subprocess

app = Flask(__name__)

@app.route('/webhook', methods=['POST'])
def handle_webhook():
    # Verify webhook source
    if verify_webhook(request):
        # Pull latest changes
        subprocess.run(['git', 'pull'])
        # Rebuild site
        subprocess.run(['mkdocs', 'build'])
        return 'OK', 200
    return 'Unauthorized', 401

Custom Endpoints

Static JSON Files

Generate JSON during build:

# mkdocs.yml
plugins:
  - gen-files:
      scripts:
        - scripts/generate_json.py
# scripts/generate_json.py
import json
import mkdocs_gen_files

data = {
    "version": "1.0.0",
    "lastUpdated": "2024-12-16",
    "pages": []
}

with mkdocs_gen_files.open("api/manifest.json", "w") as f:
    json.dump(data, f)

RSS Feed Generation

# Generate RSS feed during build
def on_post_build(config):
    from feedgen.feed import FeedGenerator
    
    fg = FeedGenerator()
    fg.title('U.WIKI Updates')
    fg.link(href=config['site_url'])
    fg.description('Latest updates from U.WIKI')
    
    # Add entries for each page
    for page in pages:
        fe = fg.add_entry()
        fe.title(page.title)
        fe.link(href=page.url)
    
    fg.rss_file('site/feed.xml')

JavaScript API Reference

Available Objects

// MkDocs provides these objects
window.app = {
    document$: /* Observable of document */,
    location$: /* Observable of location */,
    target$: /* Observable of target */,
    keyboard$: /* Observable of keyboard */,
    viewport$: /* Observable of viewport */,
    tablet$: /* Observable of tablet */,
    screen$: /* Observable of screen */,
    print$: /* Observable of print */,
    palette$: /* Observable of palette */
}

Custom Functions

// Add to docs/assets/javascripts/custom.js

// Copy code blocks with custom behavior
function initCustomCodeCopy() {
    document.querySelectorAll('pre > code').forEach(block => {
        const button = document.createElement('button');
        button.className = 'copy-button';
        button.textContent = 'Copy';
        
        button.addEventListener('click', () => {
            navigator.clipboard.writeText(block.textContent);
            button.textContent = 'Copied!';
            setTimeout(() => button.textContent = 'Copy', 2000);
        });
        
        block.parentElement.appendChild(button);
    });
}

// Initialize on page load
document.addEventListener('DOMContentLoaded', initCustomCodeCopy);

API Security

CORS Considerations

When calling external APIs:

// Handle CORS errors
async function fetchWithCORS(url) {
    try {
        const response = await fetch(url, {
            mode: 'cors',
            credentials: 'omit'
        });
        return await response.json();
    } catch (error) {
        console.error('CORS error:', error);
        // Fallback to proxy
        return fetchViaProxy(url);
    }
}

API Key Management

Never expose API keys in client-side code:

// DON'T DO THIS
const API_KEY = 'sk-1234567890'; // Exposed to everyone!

// DO THIS - Use a proxy server
async function fetchSecureData() {
    // Your server handles the API key
    const response = await fetch('/api/proxy/secure-endpoint');
    return response.json();
}

Performance Optimization

Lazy Loading

Load API data only when needed:

// Intersection Observer for lazy loading
const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            loadAPIData(entry.target);
            observer.unobserve(entry.target);
        }
    });
});

// Observe elements that need API data
document.querySelectorAll('[data-api-endpoint]').forEach(el => {
    observer.observe(el);
});

Caching

Cache API responses:

const apiCache = new Map();

async function fetchWithCache(url, ttl = 3600000) { // 1 hour default
    const cached = apiCache.get(url);
    
    if (cached && Date.now() - cached.timestamp < ttl) {
        return cached.data;
    }
    
    const data = await fetch(url).then(r => r.json());
    apiCache.set(url, { data, timestamp: Date.now() });
    
    return data;
}

Error Handling

Graceful Degradation

async function loadEnhancedContent() {
    const container = document.getElementById('enhanced-content');
    
    try {
        const data = await fetchAPIData();
        container.innerHTML = renderData(data);
    } catch (error) {
        // Fall back to static content
        container.innerHTML = `
            <div class="api-error">
                <p>Unable to load dynamic content.</p>
                <p>View <a href="/static-version">cached version</a></p>
            </div>
        `;
    }
}

Examples

Live Status Badge

// Show live system status
async function updateStatus() {
    try {
        const response = await fetch('https://api.example.com/status');
        const status = await response.json();
        
        document.getElementById('status-badge').className = `badge ${status.state}`;
        document.getElementById('status-text').textContent = status.message;
    } catch (error) {
        document.getElementById('status-badge').className = 'badge unknown';
        document.getElementById('status-text').textContent = 'Status Unknown';
    }
}

// Update every 30 seconds
setInterval(updateStatus, 30000);
updateStatus();

Dynamic Search Suggestions

// Enhance search with external API
const searchInput = document.querySelector('.md-search__input');
let searchTimeout;

searchInput.addEventListener('input', (e) => {
    clearTimeout(searchTimeout);
    
    searchTimeout = setTimeout(async () => {
        if (e.target.value.length > 2) {
            const suggestions = await fetch(`/api/search/suggest?q=${e.target.value}`)
                .then(r => r.json());
            
            displaySuggestions(suggestions);
        }
    }, 300); // Debounce 300ms
});

Next Steps