Intro
Il y a deux semaines, nous avons présenté @duocrafters/notion-database-zod, une librairie qui transforme le schéma d’une base Notion en schéma Zod strict.
Cette semaine, on passe à l’étape suivante : brancher directement une base Notion dans les Content Collections d’Astro, sans écrire de code de glue.
C’est précisément l’objectif de @duocrafters/notion-database-astro : une intégration prête à l’emploi qui combine Notion, Zod et Astro.
Le problème initial
Dans Astro, les Content Collections apportent un typage et une validation précieux… mais uniquement si la source est locale (Markdown/MDX).
Dès qu’on veut brancher une source distante (ici Notion), on retombe dans l’inconfort : aucune garantie de structure, aucun message clair si le schéma Notion change.
@duocrafters/notion-database-zod a posé la première brique : générer un schéma Zod strict directement depuis Notion.
La deuxième brique @duocrafters/notion-database-astro s’appuie dessus pour brancher ce schéma dans Astro sans boilerplate.
L’API : notionLoader
La librairie expose un unique point d’entrée :
import { notionLoader } from "@duocrafters/notion-database-astro";
C’est un loader compatible avec defineCollection d’Astro.
Il prend en paramètre la configuration Notion (auth, databaseId, éventuel databaseQuery), et en retour Astro sait charger, valider et exposer vos pages comme n’importe quelle collection Markdown.
Exemple concret
import { defineCollection } from "astro:content";
import { NOTION_BLOG_POSTS_DATABASE, NOTION_TOKEN } from "astro:env/server";
import { notionLoader } from "@duocrafters/notion-database-astro";
import blogs from "../blog.json";
export const blogPosts = defineCollection({
loader: notionLoader({
auth: NOTION_TOKEN,
databaseId: NOTION_BLOG_POSTS_DATABASE,
databaseQuery: {
filter: {
property: "Date de publication",
date: {
is_not_empty: true,
},
},
},
filter: (page) => {
return blogs.includes(
(
(page.properties.Article as unknown as { title: string })
.title[0] as unknown as { plain_text: string }
).plain_text?.trim(),
);
},
}),
});
Ce qu’il se passe :
- Connexion à Notion via le token serveur.
- Génération du schéma Zod de la base (
generateDatabaseSchemade la première librairie). - Query ciblée de la base (ici : uniquement les entrées qui ont une “Date de publication”).
- Filtrage custom côté projet (ici : ne retenir que les articles listés dans un fichier
blog.json). - Résultat : une Content Collection Astro nommée
blogPosts, avec validation automatique et typage fort.
Rendu du contenu
Exactement comme pour une collection Markdown, vous pouvez rendre le HTML d’un post Notion via la méthode render.
const post = await getEntry("blogPosts", "my-post-id");
const { Content } = await render(post);
Il suffit ensuite d’utiliser <Content /> dans vos composants Astro : le contenu Notion est injecté comme du Markdown classique, mais en conservant la validation stricte apportée par Zod.
Avantages
- Zéro boilerplate : pas besoin de manipuler manuellement le
ClientNotion ni de faire correspondre le schéma. - Validation intégrée : si quelqu’un change le schéma Notion, Astro échoue au build avec un message clair.
- Interop Astro native : le loader est compatible avec l’écosystème des Content Collections, exactement comme si vos contenus venaient d’un front-matter Markdown.
- Extensible : vous pouvez passer un
databaseQueryNotion complet et unfiltercustom en JavaScript.
Typage TypeScript
Comme la librairie repose sur zod, vous pouvez inférer le type des pages Notion exactement comme pour du Markdown :
import { z } from "zod";
import { blogPosts } from "../content/config";
type BlogPost = z.infer<(typeof blogPosts)["schema"]>;
Astro vous garantit alors que vos composants manipulent un BlogPost conforme au schéma Notion.
👉 En résumé : @duocrafters/notion-database-astro rend Notion premier citoyen des Content Collections Astro, avec validation, typage et rendu <Content /> identiques au Markdown… mais sans sacrifier la souplesse d’une base distante.


_Z1IETUX.webp)