Filter Draft Posts


Introduction

Today we are going to be adding a draft property to our blog posts. This will allow us to work on a post and keep it from showing. There are probably several ways you could use this but I am doing it so that I can have someone else review my posts as a pull request in Github. Once the review is good we can push an update to set the flag to false and approve it. I am using DevOps so, when the PR is approved, that fires off an action in GitHub that will build and deploy the code to our site. Another benefit is that you can work on several posts at the same time. I like to schedule these out a bit so that there are always 2 or 3 in the pipeline at various stages of done.

Code

To implement this we need to add a property to our schema. In astro we do this in the config.ts file. We are going to add a property called draft that is a boolean. When true it will be excluded from display on the website blog page. When false the post will be shown.

Schema update

We need to add the line draft: z.boolean().optional() in the schema object.

Here is my config.ts blog schema.

import { defineCollection, z } from 'astro:content';

const blog = defineCollection({
	type: 'content',
	// Type-check frontmatter using a schema
	schema: z.object({
		title: z.string(),
		description: z.string(),
		// Transform string to Date object
		pubDate: z.date(),
		updatedDate: z.coerce.date().optional(),
		preview: z.string(),
		draft: z.boolean().optional()
	}),
});

export const collections = { blog };

I chose to make this an optional parameter because Frontmatter CMS will remove the property from the front matter metadata when it is false.

Filter the Collection

Now that we have a property in the schema to use we need to use it in the function that fetches our blog posts. In astro, this can be found in /src/pages/blog/index.astro. There is a constant called posts that we will be modifying. We need to filter the posts based on the new draft property.

const posts = (await getCollection("blog")).sort(
  (a, b) => a.data.pubDate.valueOf() - b.data.pubDate.valueOf()
).filter(post => !!post.data.draft);

All we did here was add a call to filter(). JavaScript supports function chaining so we can add .filter(post => !!post.data.draft) to the end of the code we already had to fetch the posts. How nice! I am also using another JS oddity with !!. You may be familiar with the ! operator as the logical “not”. When we use it twice we get a way to derive the “truthyness” of a value. Remember earlier when I said we were making this an optional property? That means that it may or not be present. In JS, this usually means the property will return undefined. By using !! we can turn it into a boolean value. In fact, undefined, null, 0, -0, NaN, empty strings ('') and false are all “falsy” and will return false. This is important because we are not going to have this present when a post is not in draft status (it will be undefined).