orbiter:collections
A Vite virtual module that gives you typed access to your published content in Astro pages. Works in both static (build-time snapshot) and SSR (live queries at request time) output modes.
Setup
The module is available automatically after adding @a83/orbiter-integration to your Astro config. No additional imports needed.
Static output (default)
Content is snapshotted when astro build runs. Pages reflect the pod state at build time — republish to pick up new content.
// astro.config.mjs
import orbiter from '@a83/orbiter-integration';
export default defineConfig({
integrations: [orbiter({ pod: './content.pod' })],
}); SSR / hybrid output
When output is 'server' or 'hybrid', getCollection and friends query the POD at request time. Content changes are reflected immediately — no rebuild needed.
import node from '@astrojs/node';
import orbiter from '@a83/orbiter-integration';
export default defineConfig({
output: 'server',
adapter: node({ mode: 'standalone' }),
integrations: [orbiter({ pod: './content.pod' })],
}); API
import { getCollection, getEntry, getLocaleCollection, getLocaleEntry, locale, locales } from 'orbiter:collections'; getCollection(name)
Returns all published entries in a collection, sorted by updated_at descending.
const posts = await getCollection('posts');
// → Entry[] getEntry(collection, slug)
Returns a single entry by slug, or null if not found or not published.
const post = await getEntry('posts', 'my-first-post');
// → Entry | null getLocaleCollection(name, locale?)
Returns published entries for a specific locale. The default locale (first entry in Settings → Locales) is stored as locale = '' in the database — pass its code and it maps correctly.
const dePosts = await getLocaleCollection('posts', 'de');
// → Entry[] with locale 'de' getLocaleEntry(collection, baseSlug, locale)
Returns a locale variant of an entry. Falls back to the default locale if the translation doesn't exist yet.
const post = await getLocaleEntry('posts', 'my-post', 'de');
// → German variant if it exists, otherwise default locale locale and locales
import { locale, locales } from 'orbiter:collections';
// locale → 'en' (default locale from Settings)
// locales → ['en', 'de'] (all configured locales) Entry shape
{
id: string, // UUID
slug: string, // URL-safe identifier
status: 'published',
created_at: string, // ISO datetime
updated_at: string, // ISO datetime
data: {
// all fields from your schema, e.g.:
title: string,
body: string, // richtext → rendered HTML
image: string, // media field → UUID
tags: string[], // array field
author: Entry, // relation → resolved Entry object
}
} Static paths
---
export async function getStaticPaths() {
const posts = await getCollection('posts');
return posts.map(post => ({ params: { slug: post.slug } }));
}
const post = await getEntry('posts', Astro.params.slug);
--- Multilingual static paths
---
import { getCollection, locales } from 'orbiter:collections';
export async function getStaticPaths() {
const posts = await getCollection('posts'); // default locale only
return posts.flatMap(post =>
locales.map(loc => ({
params: { slug: post.slug, lang: loc }
}))
);
}
--- Media URLs
<img src={`/orbiter/media/${post.data.image}`} alt="" /> Works regardless of which media backend is configured. For external backends, /orbiter/media/[id] issues a 302 redirect to the CDN or original URL.
orbiter:db
A second virtual module that exposes the resolved pod path for use in your own Astro server routes or API endpoints.
import { podPath } from 'orbiter:db';
import { openPod } from '@a83/orbiter-core';
// src/pages/api/my-endpoint.js
export async function GET() {
const db = openPod(podPath);
const entries = db.getEntries('posts');
db.close();
return new Response(JSON.stringify(entries), {
headers: { 'Content-Type': 'application/json' }
});
} orbiter:db only works in server-rendered routes (output: 'server' or 'hybrid'). In static output there is no runtime to open the pod.Relation fields
Relation fields are resolved automatically — at build time in static mode, at request time in SSR mode. The raw UUID reference is replaced with the full Entry object of the related entry.
{posts.map(post => (
<div>
<h2>{post.data.title}</h2>
<p>by {post.data.author?.data?.name}</p>
{post.data.categories?.map(cat => (
<span>{cat.data.name}</span>
))}
</div>
))}