Skip to content

Content Fetching

The SDK provides multiple ways to fetch content from Aether, optimized for different use cases.

MethodWhen to UsePerformance
fetchSection()Server-side, one sectionFast
fetchPage()Server-side, full pageModerate
ContentManagerClient-side with cachingSmart
getAetherSection()Astro build-timeFastest

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 autocomplete
const hero = await fetch.section(SECTIONS.HERO, config);
// ^? const hero: Section<HeroData>
// Full autocomplete for all fields!
console.log(hero.data.title); // ✅ TypeScript knows this exists
console.log(hero.data.subtitle); // ✅ TypeScript knows this exists
console.log(hero.data.invalidField); // ❌ TypeScript error!
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 types
const section = await fetch.section(SECTIONS.HERO, config);
// Or with string ID (no type safety)
const section2 = await fetch.section('hero-section-id', config);

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 section
const components = await fetch.components(SECTIONS.HERO, config);
// Filter by component type/slug
const buttons = components.filter(c => c.component.slug === 'button');
const statCards = components.filter(c => c.component.slug === 'stat-card');
// Access component data
buttons.forEach(btn => {
console.log(btn.data.label, btn.data.url);
});
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 orderIndex
page.sections.forEach(section => {
console.log(section.id, section.data);
});
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 API
const 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'
});
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,
});
import { getField } from '@aether-official/sites-sdk/content';
const section = await fetchSection('hero-123', config);
// Get nested values
const title = getField(section, 'hero.title');
const ctaText = getField(section, 'hero.cta.text');
// Array access
const firstMember = getField(section, 'team.members.0.name');
interface HeroData {
hero: {
title: string;
subtitle: string;
cta: {
text: string;
url: string;
};
};
}
const section = await fetchSection<HeroData>('hero-123', config);
// TypeScript knows the structure
const title: string = section.data.hero.title;
const ctaUrl: string = section.data.hero.cta.url;
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);
});
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;
---
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>
---
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>
))}
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;
}
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
}
}
}
// Fetch multiple sections in parallel
const [hero, features, team] = await Promise.all([
fetchSection('hero-123', config),
fetchSection('features-456', config),
fetchSection('team-789', config),
]);
app/page.tsx
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>
);
}
// Warm cache on app startup
import { createContentManager } from '@aether-official/sites-sdk';
const content = createContentManager(config);
// Preload critical sections
await Promise.all([
content.getSection('hero-123'),
content.getSection('features-456'),
content.getSection('nav-menu'),
]);