logo

アルパカログ

easy-notion-blogにいいねボタンを追加してNotion DBを更新する

ブログを書く人にとって読者からのフィードバックは何よりのモチベーションになります。

そこでこの記事では、easy-notion-blog にいいねボタンを追加しNotion DBに追加したLike列(プロパティ)を増やすカスタマイズの方法を説明します。

STEP 0. 必要なライブラリのインストール

easy-notion-blog バージョン0.9.0以降をお使いの方はこの手順は不要です。

そうでない方は axios というライブラリをインストールしましょう。

yarn add axios

インストールしたら package.jsonyarn.lock をコミットしておきましょう。

STEP 1. Notion DBにLike列を追加する

Notionを開いてブログのDBにLike列を Type: Number として追加しましょう。

画像が読み込まれない場合はページを更新してみてください。
Likeプロパティを追加しよう
STEP 2. ブログのソースコードをLike列に対応する

DBに追加した Like プロパティをブログのソースでも扱えるように変更していきます。

まず src/lib/notion/interfaces.ts を開き Post インターフェースに Like プロパティを追加します。

  export interface Post {
    PageId: string
    Title: string
    Slug: string
    Date: string
    Tags: string[]
    Excerpt: string
    OGImage: string
    Rank: number
+   Like: number
  }
src/lib/notion/interfaces.ts を開いて Post に Like: number を追加しよう

これで Post インターフェースで Like が扱えるようになりました。

次に、DBから取得した Like を実際に Post へ設定します。

src/lib/notion/client.ts を開き _buildPost メソッドを探します。

_buildPost の中で Post を作成しているので、そこに Like プロパティを追加しましょう。

  const post: Post = {
    PageId: data.id,
    Title: prop.Page.title[0].plain_text,
    Slug: prop.Slug.rich_text[0].plain_text,
    Date: prop.Date.date.start,
    Tags: prop.Tags.multi_select.map(opt => opt.name),
    Excerpt:
      prop.Excerpt.rich_text.length > 0
        ? prop.Excerpt.rich_text[0].plain_text
        : '',
    OGImage:
      prop.OGImage.files.length > 0 ? prop.OGImage.files[0].file.url : null,
    Rank: prop.Rank.number,
+   Like: prop.Like.number,
  }

  return post
src/lib/notion/client.ts を開いて Like プロパティの値を受け取れるように変更しよう

これでブログのソースで実際の Like プロパティの値が扱えるようになりました。

STEP 3. Likeプロパティを更新するためのメソッドを定義する

今回はいいねボタンを押したときに Like プロパティを増やしたいので、 Like を更新できるようにしなければなりません。

src/lib/notion/client.ts を開き、 Like プロパティを更新するためのメソッド incrementLikes を追加しましょう。

+ export async function incrementLikes(post:Post) {
+   const result = await client.pages.update({
+     page_id: post.PageId,
+     properties: {
+       'Like': (post.Like || 0) + 1,
+     },
+   })
+ 
+   if (!result) {
+     return null
+   }
+ 
+   return _buildPost(result)
+ }
+ 
src/lib/notion/client.ts を開いて Like プロパティを更新するメソッドを追加しよう

incrementLikespost を受け取り、現在の Like に +1 した値でNotion DBを更新します。

ただし、 Like の値がまだない場合は null が入っているため、 (post.Like || 0) のようにして null の場合には 0 + 1 となるようにしています。

📝
値を加算することをインクリメントと言います。特に、現在の値に対して +1 する場合に使われることが多いです。インクリメントの対義語はデクリメントです。
📝
a || b のように書くと左辺 a が偽(falsy)の場合に右辺 b の値の評価結果を返します。 null は falsy な値のため、右辺が評価されて 0 が返ります。左辺 a が真(truthy)な場合には右辺は評価されず左辺の値が返ります。
STEP 4. LikeをインクリメントするAPIを作る

Like をインクリメントするメソッドを作ったので、いいねボタンを押したときに直接呼び出せば良いような気がしますが、これはできません。

JavaScript がアクセスできるのは、その JavaScript が生成されたドメインと同じドメインだけという決まりがあります。これを Same-Origin Policy(同一生成元ポリシー)と言います。

Notion API は notion.so ドメインで、私たちが Vercel でホストしている easy-notion-blog とは別ドメインです。

そのため、フロントの JavaScript から直接 Notion API にアクセスすることはできません。

画像が読み込まれない場合はページを更新してみてください。
フロントの JavaScript から直接 Notion API をリクエストすることは Same-Origin Policy 違反となる

そこで、easy-notion-blog のサーバーサイドを経由して Notion API にアクセスします。

いいねボタン(フロント)を押した際にいったん easy-notion-blog のサーバーサイドにリクエストし、サーバーサイドから Notion API をリクエストするという作戦です。

画像が読み込まれない場合はページを更新してみてください。
easy-notion-blog のサーバー経由でいいね

そういうわけで easy-notion-blog にいいねを受け付ける API を作ります。

src/pages/api ディレクトリを作成し下記の内容で src/pages/api/like.ts を作成します。

import { NextApiRequest, NextApiResponse } from 'next'

import {
  getPostBySlug,
  incrementLikes,
} from '../../lib/notion/client'

const ApiLike = async function(req: NextApiRequest, res: NextApiResponse) {
  if (req.method !== 'PUT') {
    res.statusCode = 400
    res.end()
    return
  }

  const { slug } = req.query

  if (!slug) {
    res.statusCode = 400
    res.end()
    return
  }

  try {
    const post = await getPostBySlug(slug as string)
    if (!post) {
      throw new Error(`post not found. slug: ${slug}`)
    }

    await incrementLikes(post)

    res.statusCode = 200
    res.end()
  } catch (e) {
    console.log(e)
    res.statusCode = 500
    res.end()
  }
}

export default ApiLike
src/pages/api/like.ts に API を定義しよう

/api/like?slug=aaa のようなURLでリクエストすることでいいねを更新できるようになりました。

今回は更新にあたるのでHTTPメソッドとしては PUT だけを許可しています。

STEP 5. いいねボタンのコンポーネントを作る

いいねAPIができたので、いよいよいいねボタンを作っていきます。

下記の内容で src/components/like-button.tsx を作成します。

import React, { useState } from 'react';
import axios from 'axios'

type Props = {
  slug: string
}

const LikeButton = (props: Props) => {
  const [active, setActive] = useState(false)

  const handleClick = () => {
    if (!active) {
      axios.put(`/api/like?slug=${props.slug}`, {})
      setActive(true)
    }
  }

  return (
    <button onClick={handleClick}>Like</button>
  )
}

export default LikeButton
src/components/like-button.tsx にいいねボタンのコンポーネントを作ろう

button に設定した onClick によって、ボタンを押したときに /api/like?slug=aaa に PUT リクエストを送ることができるようになりました。

STEP 6. いいねボタンを設置する

ボタンのコンポーネントができたので記事ページの下部に設置しましょう。

src/pages/blog/[slug].tsx を開いて <footer>SocialButtons の下あたりに追加してみましょう。

ファイルの上部でインポートするのも忘れないでください。

+ import LikeButton from '../../components/like-button'
src/pages/blog/[slug].tsx の上部で LikeButton を読み込む
  <footer>
    {NEXT_PUBLIC_URL && (
      <SocialButtons
        title={post.Title}
        url={new URL(
          getBlogLink(post.Slug),
          NEXT_PUBLIC_URL
        ).toString()}
        id={post.Slug}
      />
    )}
+   <LikeButton slug={post.Slug} />
  </footer>
記事のフッターにボタンを追加しよう

追加できたらローカルの開発環境を立ち上げて動作確認してみましょう。

yarn dev

ブラウザで http://localhost:3000/blog にアクセスしていいねボタンの動作を確認しましょう。

ボタンを押したときに、該当ポストの Like プロパティが増えていれば成功です。

もしエラーになってしまったり動作しない場合は、 yarn dev やブラウザのコンソールログを確認してみてください。

それでもわからない場合は Twitter の easy-notion-blog コミュニティ に参加して気軽に聞いてみてください。私もチェックしているので力になれるはずです。

以上です。

この記事では、easy-notion-blog にいいねボタンを追加しNotion DBに追加したLikeプロパティを増やすカスタマイズの方法を説明しました。