Skip to content

Commit 419ada2

Browse files
authored
fix: handle revalidate on post delete (#94)
Co-authored-by: Cody Olsen <[email protected]>
1 parent e43759b commit 419ada2

File tree

4 files changed

+60
-30
lines changed

4 files changed

+60
-30
lines changed

pages/api/revalidate.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,14 +42,13 @@ export default async function revalidate(
4242
return res.status(401).send(message)
4343
}
4444

45-
const { _id, _type } = body
46-
if (typeof _id !== 'string' || !_id) {
45+
if (typeof body._id !== 'string' || !body._id) {
4746
const invalidId = 'Invalid _id'
4847
console.error(invalidId, { body })
4948
return res.status(400).send(invalidId)
5049
}
5150

52-
const staleRoutes = await queryStaleRoutes({ _id, _type })
51+
const staleRoutes = await queryStaleRoutes(body as any)
5352
await Promise.all(staleRoutes.map((route) => res.revalidate(route)))
5453

5554
const updatedRoutes = `Updated routes: ${staleRoutes.join(', ')}`
@@ -64,10 +63,33 @@ export default async function revalidate(
6463
type StaleRoute = '/' | `/posts/${string}`
6564

6665
async function queryStaleRoutes(
67-
body: Pick<ParseBody['body'], '_type' | '_id'>
66+
body: Pick<ParseBody['body'], '_type' | '_id' | 'date' | 'slug'>
6867
): Promise<StaleRoute[]> {
6968
const client = createClient({ projectId, dataset, apiVersion, useCdn: false })
7069

70+
// Handle possible deletions
71+
if (body._type === 'post') {
72+
const exists = await client.fetch(groq`*[_id == $id][0]`, { id: body._id })
73+
if (!exists) {
74+
let staleRoutes: StaleRoute[] = ['/']
75+
if ((body.slug as any)?.current) {
76+
staleRoutes.push(`/posts/${(body.slug as any).current}`)
77+
}
78+
// Assume that the post document was deleted. Query the datetime used to sort "More stories" to determine if the post was in the list.
79+
const moreStories = await client.fetch(
80+
groq`count(
81+
*[_type == "post"] | order(date desc, _updatedAt desc) [0...3] [dateTime(date) > dateTime($date)]
82+
)`,
83+
{ date: body.date }
84+
)
85+
// If there's less than 3 posts with a newer date, we need to revalidate everything
86+
if (moreStories < 3) {
87+
return [...new Set([...(await queryAllRoutes(client)), ...staleRoutes])]
88+
}
89+
return staleRoutes
90+
}
91+
}
92+
7193
switch (body._type) {
7294
case 'author':
7395
return await queryStaleAuthorRoutes(client, body._id)

schemas/author.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,24 @@
11
import { UserIcon } from '@sanity/icons'
2-
import { defineType } from 'sanity'
2+
import { defineField, defineType } from 'sanity'
33

44
export default defineType({
55
name: 'author',
66
title: 'Author',
77
icon: UserIcon,
88
type: 'document',
99
fields: [
10-
{
10+
defineField({
1111
name: 'name',
1212
title: 'Name',
1313
type: 'string',
1414
validation: (Rule) => Rule.required(),
15-
},
16-
{
15+
}),
16+
defineField({
1717
name: 'picture',
1818
title: 'Picture',
1919
type: 'image',
2020
options: { hotspot: true },
2121
validation: (Rule) => Rule.required(),
22-
},
22+
}),
2323
],
2424
})

schemas/post.ts

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BookIcon } from '@sanity/icons'
2-
import { defineType } from 'sanity'
2+
import { format, parseISO } from 'date-fns'
3+
import { defineField, defineType } from 'sanity'
34

45
import authorType from './author'
56

@@ -21,62 +22,69 @@ export default defineType({
2122
icon: BookIcon,
2223
type: 'document',
2324
fields: [
24-
{
25+
defineField({
2526
name: 'title',
2627
title: 'Title',
2728
type: 'string',
2829
validation: (Rule) => Rule.required(),
29-
},
30-
{
30+
}),
31+
defineField({
3132
name: 'slug',
3233
title: 'Slug',
3334
type: 'slug',
3435
options: {
3536
source: 'title',
3637
maxLength: 96,
38+
isUnique: (value, context) => context.defaultIsUnique(value, context),
3739
},
3840
validation: (Rule) => Rule.required(),
39-
},
40-
{
41+
}),
42+
defineField({
4143
name: 'content',
4244
title: 'Content',
4345
type: 'array',
4446
of: [{ type: 'block' }],
45-
},
46-
{
47+
}),
48+
defineField({
4749
name: 'excerpt',
4850
title: 'Excerpt',
4951
type: 'text',
50-
},
51-
{
52+
}),
53+
defineField({
5254
name: 'coverImage',
5355
title: 'Cover Image',
5456
type: 'image',
5557
options: {
5658
hotspot: true,
5759
},
58-
},
59-
{
60+
}),
61+
defineField({
6062
name: 'date',
6163
title: 'Date',
6264
type: 'datetime',
63-
},
64-
{
65+
initialValue: () => new Date().toISOString(),
66+
}),
67+
defineField({
6568
name: 'author',
6669
title: 'Author',
6770
type: 'reference',
6871
to: [{ type: authorType.name }],
69-
},
72+
}),
7073
],
7174
preview: {
7275
select: {
7376
title: 'title',
7477
author: 'author.name',
78+
date: 'date',
7579
media: 'coverImage',
7680
},
77-
prepare(selection) {
78-
const { author } = selection
79-
return { ...selection, subtitle: author && `by ${author}` }
81+
prepare({ title, media, author, date }) {
82+
const subtitles = [
83+
author && `by ${author}`,
84+
date && `on ${format(parseISO(date), 'LLL d, yyyy')}`,
85+
].filter(Boolean)
86+
87+
return { title, media, subtitle: subtitles.join(' ') }
8088
},
8189
},
8290
})

schemas/settings.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CogIcon } from '@sanity/icons'
2-
import { defineType } from 'sanity'
2+
import { defineField, defineType } from 'sanity'
33

44
export default defineType({
55
name: 'settings',
@@ -9,13 +9,13 @@ export default defineType({
99
// Uncomment below to have edits publish automatically as you type
1010
// liveEdit: true,
1111
fields: [
12-
{
12+
defineField({
1313
name: 'title',
1414
description: 'This field is the title of your blog.',
1515
title: 'Title',
1616
type: 'string',
1717
initialValue: 'Blog.',
1818
validation: (rule) => rule.required(),
19-
},
19+
}),
2020
],
2121
})

0 commit comments

Comments
 (0)