AT Protocol-powered loaders for Astro content collections.
This package exposes:
atLoader(...)for build-time/static collection syncingatLiveLoader(...)for Astro live collectionsatZodSchema(...)to derive a Zod schema from a generated lexicon record schema
It is designed for lexicons generated by @atproto/lex.
npm i at-astro-loader
# or
pnpm add at-astro-loader
# or
bun add at-astro-loaderGenerate the record schema types you want to load:
npx -p @atproto/lex lex install <nsid...>
npx -p @atproto/lex lex buildThis typically generates modules under src/lexicons (or your configured output directory).
import { defineCollection } from "astro:content";
import { atLoader } from "at-astro-loader";
import * as app from "../src/lexicons/app";
export const collections = {
posts: defineCollection({
loader: atLoader(app.bsky.feed.post, {
repo: "myhandle.com",
endpoint: "https://public.api.bsky.app",
limit: 100,
reverse: true,
}),
}),
};Creates a regular Astro content loader (Loader) that fetches records via client.list(...) during content sync.
- Clears the store on each load (
store.clear()) - Validates/coerces each record with Astro
parseData() - Stores entries keyed by
record.cid - Uses
generateDigest(data)for digest - Throws
ATLoaderErrorif invalid records are returned
Example:
import { defineCollection } from "astro:content";
import { atLoader } from "at-astro-loader";
import * as app from "../src/lexicons/app";
const posts = defineCollection({
loader: atLoader(app.bsky.feed.post, {
repo: "myhandle.com",
limit: 100,
reverse: true,
}),
});Creates an Astro LiveLoader backed by an ATProto record schema.
loadEntrydelegates toclient.get(...)loadCollectiondelegates toclient.list(...)- Returns entry IDs as CIDs
- Returns
ATLoaderErrorin the live loader response error channel
Example:
import { defineCollection } from "astro:content";
import { atLiveLoader } from "at-astro-loader";
import * as app from "../src/lexicons/app";
const posts = defineCollection({
loader: atLiveLoader(app.bsky.feed.post, {
repo: "myhandle.com",
endpoint: "https://public.api.bsky.app",
}),
});Builds a Zod schema using the lexicon schema safeParse, useful when you need schema reuse outside atLoader.
import { atZodSchema } from "at-astro-loader";
import * as app from "../src/lexicons/app";
const postSchema = atZodSchema(app.bsky.feed.post);ATLoaderConfig extends ATProto CallOptions and supports:
repo?: AtIdentifierString
DID or handle. Defaults to the authenticated user's DID (ATProto client behavior).client?: Client
Provide a preconfigured client (auth/session/custom headers).endpoint?: string
Defaults tohttps://public.api.bsky.app.
Any additional CallOptions/list-get options are forwarded to the underlying client calls.
All exported helpers accept either:
- a generated schema object
T - or a namespace wrapper
{ main: T }
This supports both direct exports and namespace module patterns from generated lexicons.
ATLoaderError is used for loader failures:
- Static loader (
atLoader) throws when list returns invalid records. - Live loader (
atLiveLoader) returns{ error: ATLoaderError }when:- a fetched record has no CID
- list returns invalid records
- If you don't pass
client, one is created lazily from@atproto/lex. - Default service endpoint is public Bluesky API:
https://public.api.bsky.app. - Entry IDs are CIDs from ATProto records.