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:
# 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
});