ハートを送れるボタンをつけました
本ブログに、記事ごとのハートボタンを追加しました👏
一番下の方につけており、押すとハート数が増えるようになっています💞
右側の数字で、いままで何人がハートボタンをクリックしたかが確認できます!
参考にした記事
今回の実装では、おとよさんの 「astro-notion-blog にいいねボタンを追加する」記事を参考にしました。
この記事では、Notion の Like プロパティを使ったいいねボタンの実装方法が紹介されています。
incrementLikes 関数で Like を +1 して Notion に書き戻すというシンプルな構成です。
// 参考記事の incrementLikes(簡略版)
export async function incrementLikes(post: Post): Promise<Post | null> {
const params: requestParams.UpdatePage = {
page_id: post.PageId,
properties: {
Like: {
number: (post.Like || 0) + 1,
},
},
}
const result = (await client.pages.update(
params as any
)) as responses.UpdatePageResponse
if (!result) {
return null
}
return _buildPost(result)
} この実装をベースに動かしてみたところ、ひとつ問題が見つかりました。
問題: キャッシュによるLike値のズレ
astro-notion-blog では、getAllPosts()の結果をメモリキャッシュ(postsCache)に保持しています。
getPostBySlug() もこのキャッシュを経由するため、API で取得した `post.Like` はビルド時やキャッシュ生成時の値になっていることがあります。
たとえば、こんなケースです。
- ビルド時に
Like = 5でキャッシュされる - 誰かがハートを押して Notion 上では
Like = 8になる - API の GET で
post.Likeを返すと、キャッシュ上の5が返る - POST で
post.Like + 1 = 6を書き込み、Notion 上の8が6に巻き戻る
ハートを押したのに数が減る、という状態になりかねません。
解決策: キャッシュをバイパスして最新値を取得する
この問題を避けるために、いいね関連の処理ではキャッシュを使わず、
Notion API を直接呼び出して最新値を取得するようにしました。
最新のハート数を取得する
pages.retrieve で対象ページを直接取得し、Like プロパティの値を返しています。
export async function getLatestLikes(post: Post): Promise<number> {
const page = (await client.pages.retrieve({
page_id: post.PageId,
})) as responses.PageObject
const latestPost = _buildPost(page)
return latestPost.Like || 0
} ハートを1件増やす
incrementLikes 自体は参考記事と同じシンプルな形にしています。渡された post.Like を +1 して更新するだけです。
export async function incrementLikes(post: Post): Promise<Post | null> {
const params: requestParams.UpdatePage = {
page_id: post.PageId,
properties: {
Like: {
number: (post.Like || 0) + 1,
},
},
}
const result = (await client.pages.update(
params as any
)) as responses.UpdatePageResponse
if (!result) {
return null
}
return _buildPost(result)
} API側で最新値を直接取得する
キャッシュの問題は API ルート側で対処しています。POST 時に getLatestLikes で最新値を取得してから incrementLikes に渡すことで、古い値での上書きを防いでいます。
// likes.json.ts(POST部分)
const post = await getPostBySlug(slug)
if (!post) {
return new Response(null, { status: 404 })
}
// キャッシュをバイパスして最新のLike値を取得してからインクリメント
const latestLikes = await getLatestLikes(post)
const updatedPost = await incrementLikes({ ...post, Like: latestLikes }) GET 側も同様に getLatestLikes を使い、常に Notion から最新値を返すようにしています。
この実装のよかった点
Astroの静的なよさを崩さず、ハート押したときの動作が不安定にならない
記事本体は静的配信のままにして、ハート数だけ動的に扱えるので、シンプルに実装できます。
ボタンを押した後に数が増えない・減るといった不具合も回避できました。
気をつける点
この実装は、同時に複数人が押した場合に取りこぼす可能性があります。
たとえば同じタイミングで2人が押すと、
- どちらも
Like = 10を取得 - どちらも
11を書き込む
となって、本来 12 になるはずが 11 のままになることが考えられます。
個人ブログではそこまで問題にならないと思ってこの形にしましたが、厳密にカウントしたい場合はNotionでLikeを管理するのではなく、更新競合を扱いやすいDBを使った方がいいかもしれません。
まとめ
参考記事のシンプルな実装をベースに、キャッシュによるLike値のズレという問題に対処しました。
-
incrementLikesは参考記事と同じシンプルな形を維持 - API 側で
getLatestLikesを使い、常に Notion から最新値を取得してからインクリメント - 静的な本文表示は Astro に任せ、動的なハート数は Notion API で取得・更新
参考記事がなければここまでスムーズに実装できなかったと思います。ありがとうございました。
↓ この機会にぜひ、♡をポチッとお願いします!