diff --git a/src/content/docs/en/reference/content-loader-reference.mdx b/src/content/docs/en/reference/content-loader-reference.mdx index f9b934faa653f..1b35d7adbeac2 100644 --- a/src/content/docs/en/reference/content-loader-reference.mdx +++ b/src/content/docs/en/reference/content-loader-reference.mdx @@ -5,6 +5,7 @@ sidebar: i18nReady: true --- import Since from '~/components/Since.astro'; +import ReadMore from '~/components/ReadMore.astro'; Astro's Content Loader API allows you to load your data from any source, local or remote, and interact with Astro's content layer to manage your [content collections](/en/guides/content-collections/). @@ -176,7 +177,7 @@ const countries = defineCollection({ ### Object loaders -A loader is an object with a `load()` method that is called at build time to fetch data and update the data store. It allows entries to be updated incrementally, or for the store to be cleared only when necessary. It can also define a schema for the entries, which can be used to validate the data and generate static types. +A loader is an object with a [`load()` method](#load) that is called at build time to fetch data and update the data store. It allows [entries](#dataentry) to be updated incrementally, or for the store to be cleared only when necessary. It can also define a schema for the entries, which can be used to validate the data and generate static types. The recommended pattern is to define a function that accepts configuration options and returns the loader object, in the same way that you would normally define an Astro integration or Vite plugin. @@ -222,9 +223,78 @@ const blog = defineCollection({ }); ``` +### Live loaders + +A live loader is an object with two methods: `loadCollection()` and `loadEntry()` that should handle errors gracefully and return either data or an `Error` object. + +```ts title="src/article-loader.ts" +import type { LiveLoader } from 'astro/loaders'; +import { fetchFromCMS } from './cms-client.js'; + +interface Article { + id: string; + title: string; + content: string; + author: string; +} + +export function articleLoader(config: { apiKey: string }): LiveLoader
{ + return { + name: 'article-loader', + loadCollection: async ({ filter }) => { + try { + const articles = await fetchFromCMS({ + apiKey: config.apiKey, + type: 'article', + filter, + }); + + return { + entries: articles.map((article) => ({ + id: article.id, + data: article, + })), + }; + } catch (error) { + return { + error: new Error(`Failed to load articles: ${error.message}`), + }; + } + }, + loadEntry: async ({ filter }) => { + try { + // filter will be { id: "some-id" } when called with a string + const article = await fetchFromCMS({ + apiKey: config.apiKey, + type: 'article', + id: filter.id, + }); + + if (!article) { + return { + error: new Error('Article not found'), + }; + } + + return { + id: article.id, + data: article, + }; + } catch (error) { + return { + error: new Error(`Failed to load article: ${error.message}`), + }; + } + }, + }; +} +``` + +[See the `Content Collection` guide](/en/guides/content-collections/#creating-a-live-loader) for more information about creating a live loader and example usage. + ## Object loader API -The API for [inline loaders](#inline-loaders) is very simple, and is shown above. This section shows the API for defining an object loader. +The API for [inline loaders](#inline-loaders) is very simple, and is shown above. This section shows the API for defining an [object loader](#object-loaders). ### The `Loader` object @@ -670,3 +740,67 @@ The format of the `RenderedContent` object is: ``` If the entry has Markdown content then you can use the [`renderMarkdown()`](#rendermarkdown) function to generate this object from the Markdown string. + +## Live loader API + +This section shows the API for defining a [live loader](#live-loaders). + +### The `LiveLoader` object + +A generic type to provide [type safety in your loader](/en/guides/content-collections/#type-safety-in-live-loaders). This describes an object and accepts four type parameters to describe, in this order: your data structure, your filters when querying a collection, your filters when querying a single entry, and errors. + +This object contains the following properties: + +#### `name` + +

+ +**Type:** `string` +

+ + +A unique name for the loader, used in logs. + +#### `loadCollection()` + +

+ +**Type:** `(context: LoadCollectionContext) => Promise | { error: TError; }>` +

+ +Defines a method to load a collection of entries. This function receives a [context object](#loadcollectioncontext) containing an optional `filter` property and must return a the data associated to this collection or the errors. + +#### `loadEntry()` + +

+ +**Type:** `(context: LoadEntryContext) => Promise | undefined | { error: TError; }>` +

+ +Defines a method to load a single entry. This function receives a [context object](#loadentrycontext) containing a `filter` property and returns either the data associated to the requested entry, `undefined` when the entry cannot be found, or the errors. + +### `LoadCollectionContext` + +This object is passed to the [`loadCollection()` method](#loadcollection) of the loader and contains the following properties: + +#### `filter` + +

+ +**Type:** `Record` +

+ +An object describing the filters supported by your loader. + +### `LoadEntryContext` + +This object is passed to the [`loadEntry()` method](#loadentry) of the loader and contains the following properties: + +#### `filter` + +

+ +**Type:** `Record` +

+ +An object describing the filters supported by your loader. diff --git a/src/content/docs/en/reference/modules/astro-content.mdx b/src/content/docs/en/reference/modules/astro-content.mdx index 1066994323bbd..535305b245c3e 100644 --- a/src/content/docs/en/reference/modules/astro-content.mdx +++ b/src/content/docs/en/reference/modules/astro-content.mdx @@ -44,7 +44,7 @@ import {

-`defineCollection()` is a utility to configure a collection in a `src/content.config.*` file. +A utility to configure a collection in a `src/content.config.*` file. ```ts title="src/content.config.ts" import { z, defineCollection } from 'astro:content'; @@ -73,7 +73,7 @@ This function accepts the following properties:

-A `loader` is either an object or a function that allows you to load data from any source, local or remote, into content collections. +Either an object or a function that allows you to load data from any source, local or remote, into a build-time content collection. (For live collections, see the [live `loader`](#loader-1) property.) [See the `Content Collection` guide](/en/guides/content-collections/#build-time-collection-loaders) for example usage. @@ -85,7 +85,7 @@ A `loader` is either an object or a function that allows you to load data from a

-`schema` is an optional Zod object to configure the type and shape of document frontmatter for a collection. Each value must use [a Zod validator](https://github.com/colinhacks/zod). +An optional Zod object or function that returns a Zod object to configure the type and shape of document frontmatter for a collection. Each value must use [a Zod validator](https://github.com/colinhacks/zod). (For live collections, see the [live `schema`](#schema-1) property.) [See the `Content Collection` guide](/en/guides/content-collections/#defining-the-collection-schema) for example usage. @@ -93,20 +93,20 @@ A `loader` is either an object or a function that allows you to load data from a

-**Type:** `(input: CollectionConfig) => CollectionConfig` +**Type:** `(config: LiveCollectionConfig) => LiveCollectionConfig`

-`defineLiveCollection()` is a utility to configure a live collection in a `src/live.config.*` file. +A utility to configure a live collection in a `src/live.config.*` file. -```ts title="src/content.config.ts" +```ts title="src/live.config.ts" import { defineLiveCollection } from 'astro:content'; -import { storeLoader } from '@mystore/astro-loader'; +import { storeLoader } from '@example/astro-loader'; const products = defineLiveCollection({ loader: storeLoader({ apiKey: process.env.STORE_API_KEY, - endpoint: 'https://api.mystore.com/v1', + endpoint: 'https://api.example.com/v1', }), }); @@ -121,11 +121,11 @@ This function accepts the following properties:

-**Type:** () => Promise<Array<\{ id: string, [key: string]: any }> | Record<string, Record<string, any>>> | Loader - +**Type:** `LiveLoader` +

-A `loader` is either an object or a function that allows you to load data from any source, local or remote, into a live content collections. +An object that allows you to load data at runtime from a remote source into a live content collection. (For build-time collections, see the [build-time `loader`](#loader) property.) [See the `Content Collection` guide](/en/guides/content-collections/#creating-a-live-loader) for example usage. @@ -137,7 +137,9 @@ A `loader` is either an object or a function that allows you to load data from a

-`schema` is an optional Zod object to configure the type and shape of document frontmatter for a live collection. Each value must use [a Zod validator](https://github.com/colinhacks/zod). +An optional Zod object to configure the type and shape of your data for a live collection. Each value must use [a Zod validator](https://github.com/colinhacks/zod). (For build-time collections, see the [build-time `schema`](#schema) property.) + +When you define a schema, it will take precedence over the [live loader’s types](/en/reference/content-loader-reference/#live-loader-api) when you query the collection. [See the `Content Collection` guide](/en/guides/content-collections/#using-zod-schemas-with-live-collections) for example usage. @@ -145,16 +147,15 @@ A `loader` is either an object or a function that allows you to load data from a

-**Type:** `(collection: string) => ZodEffects`
+**Type:** (collection: CollectionKey) => ZodEffects\

-The `reference()` function is used in the content config to define a relationship, or "reference," from one collection to another. This accepts a collection name and transforms the reference into an object containing the collection name and the reference id. - +A function used in the content config to define a relationship, or "reference", from one collection to another. This accepts a collection name and transforms the reference into an object containing the collection name and the reference id. This example defines references from a blog author to the `authors` collection and an array of related posts to the same `blog` collection: -```ts +```ts title="src/content.config.ts" import { defineCollection, reference, z } from 'astro:content'; import { glob, file } from 'astro/loaders'; @@ -188,19 +189,19 @@ const relatedPosts = await getEntries(blogPost.data.relatedPosts);

-**Type:** `(collection: string, filter?: (entry: CollectionEntry) => boolean) => CollectionEntry[]` +**Type:** (collection: CollectionKey, filter?: (entry: CollectionEntry) => boolean) => CollectionEntry[]

-`getCollection()` is a function that retrieves a list of content collection entries by collection name. +A function that retrieves a list of content collection entries by collection name. It returns all items in the collection by default, and accepts an optional `filter` function to narrow by entry properties. This allows you to query for only some items in a collection based on `id` or frontmatter values via the `data` object. -```astro +```astro title="src/pages/blog/index.astro" --- import { getCollection } from 'astro:content'; -// Get all `src/content/blog/` entries +// Get all `src/data/blog/` entries const allBlogPosts = await getCollection('blog'); // Only return posts with `draft: true` in the frontmatter @@ -216,25 +217,23 @@ const draftBlogPosts = await getCollection('blog', ({ data }) => {

-**Type:** `(collection: string, filter?: (entry: CollectionEntry) => boolean) => CollectionEntry[]` +**Type:** `(collection: string, filter?: LiveLoaderCollectionFilterType) => Promise`

-`getLiveCollection()` is a function that retrieves a list of live content collection entries by collection name. +A function that retrieves a list of live content collection entries by collection name. -It returns all items in the collection by default, and accepts an optional `filter` function to narrow by entry properties. This allows you to query for only some items in a collection based on `id` or frontmatter values via the `data` object. +It returns all items in the collection by default, and accepts an optional `filter` object whose shape is defined by the collection's loader. This allows you to query for only some items in a collection or retrieve data in a different form, depending on your API's capabilities. -```astro +```astro title="src/pages/shop/index.astro" --- import { getLiveCollection } from 'astro:content'; -// Get all `src/content/blog/` entries -const allBlogPosts = await getLiveCollection('blog'); +// Get all `products` entries from your API +const allProducts = await getLiveCollection('products'); -// Only return posts with `draft: true` in the frontmatter -const draftBlogPosts = await getLiveCollection('blog', ({ data }) => { - return data.draft === true; -}); +// Only return `products` that should be featured +const featuredProducts = await getLiveCollection('products', { featured: true }); --- ``` @@ -245,14 +244,14 @@ const draftBlogPosts = await getLiveCollection('blog', ({ data }) => {

**Types:** -* `(collection: string, id: string) => Promise | undefined>` -* `({ collection: string, id: string }) => Promise | undefined>` +* (collection: CollectionKey, id: string) => Promise\<CollectionEntry | undefined\> +* (\{ collection: CollectionKey, id: string \}) => Promise\<CollectionEntry | undefined\>

-`getEntry()` is a function that retrieves a single collection entry by collection name and the entry `id`. `getEntry()` can also be used to get referenced entries to access the `data` or `body` properties: +A function that retrieves a single collection entry by collection name and the entry `id`. `getEntry()` can also be used to get referenced entries to access the `data` or `body` properties: -```astro +```astro title="src/pages/index.astro" --- import { getEntry } from 'astro:content'; @@ -273,24 +272,21 @@ See the `Content Collections` guide for examples of [querying collection entries

-**Types:** -* `(collection: string, id: string) => Promise` -* `({ collection: string, filter: LiveLoaderEntryFilterType }) => Promise` +**Type:** `(collection: string, filter: string | LiveLoaderEntryFilterType) => Promise`

-`getLiveEntry()` is a function that retrieves a single live collection entry by collection name and the entry `id`. `getLiveEntry()` can also be used to get referenced entries to access the `data` or `body` properties: +A function that retrieves a single live collection entry by collection name and an optional filter either as an `id` string or as a type-safe object. -```astro +```astro title="src/pages/blog/[id].astro" --- import { getLiveEntry } from 'astro:content'; -const enterprisePost = await getLiveEntry('blog', 'enterprise'); - -const picardProfile = await getLiveEntry('captains', 'picard'); - -// Get the profile referenced by `data.captain` -const enterpriseCaptainProfile = await getLiveEntry(enterprisePost.data.captain); +const liveCollectionsPost = await getLiveEntry('blog', Astro.params.id); +const mattDraft = await getLiveEntry('blog', { + status: 'draft', + author: 'matt', +}); --- ``` @@ -300,13 +296,13 @@ See the `Content Collections` guide for examples of [querying collection entries

-**Type:** `(Array<{ collection: string, id: string }>) => Array>` +**Type:** (\{ collection: CollectionKey, id: string \}[]) => CollectionEntry[]

-`getEntries()` is a function that retrieves multiple collection entries from the same collection. This is useful for [returning an array of referenced entries](/en/guides/content-collections/#defining-collection-references) to access their associated `data` and `body` properties. +A function that retrieves multiple collection entries from the same collection. This is useful for [returning an array of referenced entries](/en/guides/content-collections/#defining-collection-references) to access their associated `data` and `body` properties. -```astro +```astro title="src/pages/blog/enterprise/index.astro" --- import { getEntries, getEntry } from 'astro:content'; @@ -321,7 +317,7 @@ const enterpriseRelatedPosts = await getEntries(enterprisePost.data.relatedPosts

-**Type:** `(entry: CollectionEntry) => Promise` +**Type:** (entry: CollectionEntry) => Promise\

@@ -352,8 +348,6 @@ const { Content, headings, remarkPluginFrontmatter } = await render(entry); import type { CollectionEntry, CollectionKey, - ContentCollectionKey, - DataCollectionKey, SchemaContext, } from 'astro:content'; ``` @@ -366,45 +360,79 @@ Query functions including [`getCollection()`](#getcollection), [`getEntry()`](#g import type { CollectionEntry } from 'astro:content'; ``` -`CollectionEntry` is a generic type. Use it with the name of the collection you're querying. +A generic type to use with the name of the collection you're querying to represent a single entry in that collection. For example, an entry in your `blog` collection would have the type `CollectionEntry<'blog'>`. Each `CollectionEntry` is an object with the following values: #### `id` +

+ **Type:** `string` +

A unique ID. Note that all IDs from Astro's built-in `glob()` loader are slugified. #### `collection` -**Example Type:** `'blog' | 'authors' | ...` +

-The name of a collection in which entries are located. This is the name used to reference the collection in your schema, and in querying functions. +**Type:** [`CollectionKey`](#collectionkey) +

+ +The name of a collection in which entries are located. This is the name used to reference the collection in your schema and in querying functions. #### `data` +

+ **Type:** `CollectionSchema` +

An object of frontmatter properties inferred from your collection schema ([see `defineCollection()` reference](#definecollection)). Defaults to `any` if no schema is configured. #### `body` +

+ **Type:** `string` +

A string containing the raw, uncompiled body of the Markdown or MDX document. +#### `rendered` + +

+ +**Type:** `RenderedContent | undefined` +

+ +The rendered content of an entry as [stored by your loader](/en/reference/content-loader-reference/#rendered). For example, this can be the rendered content of a Markdown entry, or HTML from a CMS. + +#### `filePath` + +

+ +**Type:** `string | undefined` +

+ +The path to an entry relative to your project directory. This value is only available for local entries. + ### `CollectionKey` -

+

+ +**Example Type:** `'blog' | 'authors' | ...`
+ +

A string union of all collection names defined in your `src/content.config.*` file. This type can be useful when defining a generic function wrapping the built-in `getCollection()`. -```ts +```ts title="src/utils/collections.ts" import { type CollectionKey, getCollection } from 'astro:content'; -async function queryCollection(collection: CollectionKey) { +export async function queryCollection(collection: CollectionKey) { return getCollection(collection, ({ data }) => { return data.draft !== true; }); @@ -419,7 +447,7 @@ This includes the following property: - `image` - The `image()` schema helper that allows you [to use local images in Content Collections](/en/guides/images/#images-in-content-collections) -```ts +```ts title="src/content.config.ts" import { defineCollection, z, type SchemaContext } from "astro:content"; export const imageSchema = ({ image }: SchemaContext) =>