Content Fetching
The SDK provides multiple ways to fetch content from Aether, optimized for different use cases.
Quick Reference
Section titled “Quick Reference”| Method | When to Use | Performance |
|---|---|---|
fetchSection() | Server-side, one section | Fast |
fetchPage() | Server-side, full page | Moderate |
ContentManager | Client-side with caching | Smart |
getAetherSection() | Astro build-time | Fastest |
Type-Safe Fetching (Recommended)
Section titled “Type-Safe Fetching (Recommended)”After generating types with the CLI, use type-safe constants and get full autocomplete:
import { fetch } from '@aether-official/sites-sdk/astro';import { SECTIONS, type HeroData } from '../aether-types';import { getConfig } from '@aether-official/sites-sdk/astro';
const config = getConfig(import.meta.env);
// ✅ Type-safe with autocompleteconst hero = await fetch.section(SECTIONS.HERO, config);// ^? const hero: Section<HeroData>
// Full autocomplete for all fields!console.log(hero.data.title); // ✅ TypeScript knows this existsconsole.log(hero.data.subtitle); // ✅ TypeScript knows this existsconsole.log(hero.data.invalidField); // ❌ TypeScript error!Server-Side Fetching
Section titled “Server-Side Fetching”Fetch a Single Section
Section titled “Fetch a Single Section”import { fetch } from '@aether-official/sites-sdk/astro';import { SECTIONS } from '../aether-types';import { getConfig } from '@aether-official/sites-sdk/astro';
const config = getConfig(import.meta.env);
// With generated typesconst section = await fetch.section(SECTIONS.HERO, config);
// Or with string ID (no type safety)const section2 = await fetch.section('hero-section-id', config);Fetch Component Instances
Section titled “Fetch Component Instances”Component instances are reusable content blocks attached to sections:
import { fetch } from '@aether-official/sites-sdk/astro';import { SECTIONS } from '../aether-types';import { getConfig } from '@aether-official/sites-sdk/astro';
const config = getConfig(import.meta.env);
// Fetch all components for a sectionconst components = await fetch.components(SECTIONS.HERO, config);
// Filter by component type/slugconst buttons = components.filter(c => c.component.slug === 'button');const statCards = components.filter(c => c.component.slug === 'stat-card');
// Access component databuttons.forEach(btn => { console.log(btn.data.label, btn.data.url);});Fetch a Full Page
Section titled “Fetch a Full Page”import { fetchPage } from '@aether-official/sites-sdk';
const page = await fetchPage('homepage', { apiUrl: process.env.AETHER_API_URL, apiToken: process.env.AETHER_API_TOKEN,});
// Sections are ordered by orderIndexpage.sections.forEach(section => { console.log(section.id, section.data);});Client-Side Fetching with Caching
Section titled “Client-Side Fetching with Caching”Using ContentManager
Section titled “Using ContentManager”import { createContentManager } from '@aether-official/sites-sdk';
const content = createContentManager({ apiUrl: 'https://app.aether.com/api/trpc', apiToken: 'your-token', cacheTTL: 5 * 60 * 1000, // 5 minutes});
// First call fetches from APIconst section1 = await content.getSection('hero-123');
// Second call returns from cache (within TTL)const section2 = await content.getSection('hero-123');
// Update section (invalidates cache)await content.updateSection('hero-123', { 'hero.title': 'New Title'});Cache Configuration
Section titled “Cache Configuration”const content = createContentManager({ apiUrl: process.env.AETHER_API_URL, apiToken: process.env.AETHER_API_TOKEN,
// Cache settings cacheTTL: 10 * 60 * 1000, // 10 minutes cacheEnabled: true,
// Debug logging debug: true,});Accessing Nested Fields
Section titled “Accessing Nested Fields”Dot Notation Helper
Section titled “Dot Notation Helper”import { getField } from '@aether-official/sites-sdk/content';
const section = await fetchSection('hero-123', config);
// Get nested valuesconst title = getField(section, 'hero.title');const ctaText = getField(section, 'hero.cta.text');
// Array accessconst firstMember = getField(section, 'team.members.0.name');Type-Safe Access
Section titled “Type-Safe Access”interface HeroData { hero: { title: string; subtitle: string; cta: { text: string; url: string; }; };}
const section = await fetchSection<HeroData>('hero-123', config);
// TypeScript knows the structureconst title: string = section.data.hero.title;const ctaUrl: string = section.data.hero.cta.url;Array Fields (Repeaters)
Section titled “Array Fields (Repeaters)”Fetch and Iterate
Section titled “Fetch and Iterate”import { getArrayField } from '@aether-official/sites-sdk/content';
const section = await fetchSection('team-section', config);const members = getArrayField(section, 'team.members');
members?.forEach((member, index) => { console.log(member.name, member.role);});Type-Safe Arrays
Section titled “Type-Safe Arrays”interface TeamMember { name: string; role: string; photo: string;}
interface TeamData { team: { heading: string; members: TeamMember[]; };}
const section = await fetchSection<TeamData>('team-section', config);const members: TeamMember[] = section.data.team.members;Build-Time Fetching (Astro)
Section titled “Build-Time Fetching (Astro)”Single Section
Section titled “Single Section”---import { getAetherSection, getConfig } from '@aether-official/sites-sdk/astro';
const config = getConfig(import.meta.env);const hero = await getAetherSection('hero-123', config);---
<h1>{hero.data.hero.title}</h1>Multiple Sections
Section titled “Multiple Sections”---import { getAetherPage, getConfig } from '@aether-official/sites-sdk/astro';
const config = getConfig(import.meta.env);const page = await getAetherPage('homepage', config);---
{page.sections.map(section => ( <div data-aether-section={section.id}> <!-- Render section based on template --> </div>))}Error Handling
Section titled “Error Handling”Graceful Degradation
Section titled “Graceful Degradation”import { fetchSection, AetherAPIError } from '@aether-official/sites-sdk';
try { const section = await fetchSection('hero-123', config); return section.data;} catch (error) { if (error instanceof AetherAPIError) { console.error('API Error:', error.status, error.message);
if (error.status === 404) { // Section not found - use fallback return { hero: { title: 'Welcome', subtitle: 'Default content' } }; }
if (error.status === 401) { // Authentication failed - check API token console.error('Invalid API token'); } }
// Generic fallback return null;}Retry Logic
Section titled “Retry Logic”async function fetchWithRetry(sectionId: string, retries = 3) { for (let i = 0; i < retries; i++) { try { return await fetchSection(sectionId, config); } catch (error) { if (i === retries - 1) throw error; await new Promise(r => setTimeout(r, 1000 * Math.pow(2, i))); // Exponential backoff } }}Performance Optimization
Section titled “Performance Optimization”Parallel Fetching
Section titled “Parallel Fetching”// Fetch multiple sections in parallelconst [hero, features, team] = await Promise.all([ fetchSection('hero-123', config), fetchSection('features-456', config), fetchSection('team-789', config),]);Incremental Static Regeneration (Next.js)
Section titled “Incremental Static Regeneration (Next.js)”export const revalidate = 3600; // Revalidate every hour
export default async function HomePage() { const section = await fetchSection('hero-123', config);
return ( <section> <h1>{section.data.hero.title}</h1> </section> );}Cache Warming
Section titled “Cache Warming”// Warm cache on app startupimport { createContentManager } from '@aether-official/sites-sdk';
const content = createContentManager(config);
// Preload critical sectionsawait Promise.all([ content.getSection('hero-123'), content.getSection('features-456'), content.getSection('nav-menu'),]);Next Steps
Section titled “Next Steps”- Astro Integration - Complete Astro guide
- Visual Editor - Enable inline editing
- API Reference - Complete API docs