I am trying to set up the Content Collections for my blog in Astro. I almost immediately ran into problems with draft posts: they were missing some schema fields, and Zod didn't like that.
Digging into Zod's docs, I came up with z.discriminatedUnion, so the schema for "drafts" will be much looser than for published posts:
// src/content/config.ts
// (simplified example)
const publishedPostSchema = z.object({
title: z.string(),
author: z.enum(["John Doe", "Jane Doe"]),
dateCreated: z.string().transform((str) => new Date(str)),
description: z.string(),
draft: z.literal(null),
});
const draftPostSchema = z.object({
title: z.string().nullable(),
author: z.enum(["John Doe", "Jane Doe"]).nullable(),
dateCreated: z
.string()
.transform((str) => new Date(str))
.nullable(),
draft: z.literal(true),
});
const exampleBlogCollection = defineCollection({
schema: z.discriminatedUnion("draft", [publishedPostSchema, draftPostSchema]),
});
export const collections = {
blog: exampleBlogCollection,
};
(I am using nullable because I usually copy the frontmatter from an existing post and delete the values, but keep the fields.)
This works, but I still have two minor issues:
1. This forces me to have a literal null "draft" field on all published posts. I would prefer to enable null, false and undefined values, but z.discriminatedUnion accepts only literals on the discriminatory field. Is there a workaround to that?
)