Querying collections
A defined collection is read with functions from the astro:content module. getCollection() returns all entries, getEntry() returns one by its id, and render() turns the text of an entry into a component you can put on a page.
A list of posts
Open src/pages/posts/index.astro and replace the made-up cards with real posts:
---
import { getCollection } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
import PostCard from '../../components/PostCard.astro';
const posts = await getCollection('blog');
---
<BaseLayout title="Posts">
<h1>Posts</h1>
{posts.map((post) => (
<PostCard title={post.data.title} url={`/posts/${post.id}`} />
))}
</BaseLayout>Open /posts and there is a card for each file in src/content/blog. In the frontmatter you can use await directly, without wrapping it in an async function.
What an entry holds
| Field | Value for first-post.md |
|---|---|
post.id | first-post, the file name without the extension |
post.data | The frontmatter checked by the schema: title, pubDate, tags |
post.body | The raw Markdown text of the post |
post.collection | blog |
post.data.pubDate is already a Date object because the schema converted the string to a date.
Filtering and sorting
getCollection() takes a filter function as its second argument. Let us hide drafts and sort posts from newest to oldest:
---
import { getCollection } from 'astro:content';
const posts = (await getCollection('blog', ({ data }) => !data.draft))
.sort((a, b) => b.data.pubDate.valueOf() - a.data.pubDate.valueOf());
---Posts with draft: true in the frontmatter disappear from the list, and the newest post comes first. Sorting is the ordinary array method sort(): Astro does not guarantee the order of entries.
One entry and its text
getEntry() finds an entry by collection name and id, and render() returns a Content component with the post's HTML. Create src/pages/posts/first-post.astro:
---
import { getEntry, render } from 'astro:content';
import BaseLayout from '../../layouts/BaseLayout.astro';
const post = await getEntry('blog', 'first-post');
if (!post) throw new Error('Post not found');
const { Content } = await render(post);
---
<BaseLayout title={post.data.title}>
<h1>{post.data.title}</h1>
<Content />
</BaseLayout>Open /posts/first-post and you see the heading and the post text converted from Markdown to HTML. You will not need such a file for every post: the next lesson replaces it with a single dynamic page.
Common mistake
People write post.title instead of post.data.title and get an empty spot on the page. Everything set in the frontmatter is in post.data, while the entry itself holds service fields such as id. The editor suggests the right path when Astro's types are set up.