Skip to content

Hooks

Define hooks directly in your collection config. They run inside the local API on every operation.

src/cms/collections/posts.ts
import { defineCollection, fields } from "@/cms/core";
import { richTextToPlainText } from "@/cms/core";
export default defineCollection({
slug: "posts",
labels: { singular: "Post", plural: "Posts" },
fields: {
title: fields.text({ required: true }),
body: fields.richText(),
excerpt: fields.text({ maxLength: 300 }),
},
hooks: {
beforeCreate(data, context) {
// Auto-generate excerpt from body
if (!data.excerpt && typeof data.body === "object") {
data.excerpt = richTextToPlainText(data.body).slice(0, 180);
}
return data;
},
afterPublish(doc, context) {
context.cache?.invalidate({ tags: ["posts", `post:${doc._id}`] });
},
afterDelete(doc, context) {
context.cache?.invalidate({ tags: ["posts", `post:${doc._id}`] });
},
},
});
Hook Arguments Return
beforeCreate (data, context) Modified data
afterCreate (doc, context)
beforeUpdate (data, existing, context) Modified data
afterUpdate (doc, context)
beforeDelete (doc, context)
afterDelete (doc, context)
beforePublish (doc, context)
afterPublish (doc, context)
beforeUnpublish (doc, context)
afterUnpublish (doc, context)
beforeSchedule (doc, context)
afterSchedule (doc, context)
beforeUpsertTranslation (data, locale, existing, context) Modified data

before* hooks on create/update receive the data and can modify it before saving. All other before* hooks can throw to abort the operation. after* hooks are for side effects like cache invalidation.

beforeUpsertTranslation is the only hook that fires on upsertTranslationbeforeCreate and beforeUpdate do not fire there.

{
user: { id, role, email } | null,
operation: "create" | "update" | "delete" | ...,
collection: "posts",
timestamp: "2025-01-01T00:00:00Z",
cache: {
invalidate: ({ tags: string[] }) => void
}
}

Use context.cache.invalidate() in after* hooks to purge Astro’s route cache:

afterPublish(doc, context) {
context.cache?.invalidate({
tags: ["posts", `post:${doc._id}`]
});
},

Tag your Astro pages to match:

---
Astro.cache.set({ tags: ["posts", `post:${post._id}`] });
---