ChatGPTに聞く:Next.jsでNotion APIを使ったお知らせページを作成

自力でコードは書かず、ChatGPTを使ってToDoアプリを作っていた友人を見て、「何それ面白そう!」と思ってさっそく試してみました!今回は前から気になっていたNotion APIを使って、Next.jsでWebページ上にお知らせページを作っていきます。簡易ブログのようなものですね!

↑私が10年以上利用している会計ソフト!

完成形

お知らせ一覧ページにタイトルを表示し、クリックしたら詳細ページに飛ぶというシンプルなもの。制作時間は1時間くらいでしょうか。デザインはなしで、とりあえず表示だけを目指してみました。

コード全文はGitHubで公開しているので、似たようなものを作ろうと思っていた方は参考にしてみてください。ちなみにNext.jsに新規で追加したのは以下の5つのファイルだけです:

  • utils/notions.js
  • pages/api/news.js
  • pages/news/index.js
  • pages/news/[id].js
  • .env.local (gitignored)

とにかくChatGPT(GPT-4)が有能すぎたので、読みやすいように多少編集や補足を追加しつつ全文掲載していきます。ちなみにざっくりとした指示のみで、プロンプトエンジニアリングなんてものは試していません。

指示:Next.jsでNotion APIを使ったお知らせページを作りたい

Notion APIを使用して、Next.jsでお知らせページを作成する手順は以下のとおりです。

1. Notion Integrationを設定


NotionのIntegrationページで新しいintegrationを作成します。


(補足:Notionの新規ページを作成、Tableから表を用意)


(補足:TitleとDateプロパティーを用意し、それぞれにコンテンツ内容を記述しておく)


作成したintegrationをデータベースページに追加して、共有を有効にします。


IntegrationのページからNOTION_SECRETとデータベースのNOTION_DATABASE_IDをメモしておきます。

補足:データベースID(NOTION_DATABASE_ID)は作成したNotionのデータベースページのURLからIDを取得できます。URLが https://www.notion.so/username/xxxxxxxxxxxx?v=yyyyyyyyyyyyyyy の場合、xxxxxxxxxxxx がデータベースIDです。

2. Next.jsプロジェクトのセットアップ

npx create-next-app notion-nextjs-news --use-npm
cd notion-nextjs-news

3. Notion APIのクライアントをセットアップ

必要なパッケージをインストールします:

npm install @notionhq/client

Notionのクライアントをセットアップします:

utils/notion.js

const { Client } = require('@notionhq/client');

const notion = new Client({
  auth: process.env.NOTION_SECRET
});

export default notion;

4. お知らせデータをフェッチするAPIルートを作成

pages/api/news.js を作成して、以下のコードを追加します:

pages/api/news.js

import notion from '../../utils/notion';

export default async function handler(req, res) {
  try {
    const response = await notion.databases.query({
      database_id: process.env.NOTION_DATABASE_ID
    });

    const news = response.results.map(page => ({
      id: page.id,
      title: page.properties.Title.title[0].text.content,
      // 他のプロパティも必要に応じて追加できます
    }));

    res.status(200).json(news);
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
}

5. お知らせページを作成

pages/news/index.js ファイルを作成して、以下のコードを追加します:

pages/news/index.js

export default function NewsPage({ news }) {
  return (
    <div>
      <h1>お知らせ</h1>
      <ul>
        {news.map(item => (
          <li key={item.id}>{item.title}</li>
        ))}
      </ul>
    </div>
  );
}

export async function getServerSideProps() {
  const res = await fetch('http://localhost:3000/api/news');
  const news = await res.json();

  return {
    props: {
      news
    }
  };
}

6. 環境変数を設定

.env.local ファイルに以下の内容を追加します:

.env.local

NOTION_SECRET=your_notion_secret
NOTION_DATABASE_ID=your_notion_database_id

7. プロジェクトを実行

npm run dev

これにより、 http://localhost:3000/news にアクセスすると、Notionデータベースからお知らせの一覧が取得できるはずです。

この方法をさらに拡張して、ページの詳細表示、ページネーション、フィルタリングなどの機能を追加することも可能です。


(お知らせのタイトルがリストになって表示されました!)

指示:タイトルをクリックしたら詳細ページにリンクさせたい

Next.jsを使ってNotion APIからのデータを元に、タイトルをクリックしたら詳細ページにリンクさせるために、以下の手順を実行します:

1. リンクのコンポーネントをインポート

まず、Link コンポーネントを使ってリンクを作成します。Link コンポーネントは Next.js に内蔵されています。

pages/news/index.js

import Link from 'next/link';

2. お知らせリストでリンクを作成

pages/news/index.js 内のリストアイテムにリンクを追加します。タイトルの部分を Link コンポーネントで囲み、詳細ページのURLにリンクします。

pages/news/index.js

{news.map(item => (
    <li key={item.id}>
        <Link href={`/news/${item.id}`}>{item.title}</Link>
    </li>
))}

3. 詳細ページの作成

pages/news/[id].js ファイルを作成します。[id] の部分は動的なルートを示しています。このページは、異なるニュースアイテムのIDに基づいて異なる内容を表示します。

pages/news/[id].js

import notion from '../../utils/notion';

function NewsDetail({ newsItem }) {
    return (
        <div>
            <h1>{newsItem.title}</h1>
            {/* 他の詳細情報を表示するコードを追加できます */}
        </div>
    );
}

export async function getServerSideProps(context) {
    const { id } = context.params;

    const response = await notion.pages.retrieve({ page_id: id });
    const newsItem = {
        id: response.id,
        title: response.properties.Title.title[0].text.content,
        // 必要に応じて他のプロパティを追加
    };

    return {
        props: {
            newsItem
        }
    };
}

export default NewsDetail;

getServerSideProps で、URLからニュースアイテムのIDを取得し、そのIDに基づいてNotion APIから詳細情報を取得しています。

以上で、お知らせリストの各アイテムをクリックすると詳細ページに遷移し、詳細情報が表示されるようになります。

指示:Notionのデータベースにある「Date」プロパティーを取得して表示したい

Notionのデータベースから「Date」プロパティを取得して表示するためには、以下の手順を行います。

1. APIでDateプロパティを取得

まず、Notion APIを使ってデータベースから「Date」プロパティを取得する必要があります。APIのレスポンスには、該当するデータベースアイテムの「Date」プロパティの情報が含まれているはずです。

2. Dateプロパティのパース

取得した「Date」プロパティは、次のような形式で返されることがよくあります:

{
    "id": "some-id",
    "properties": {
        "Date": {
            "type": "date",
            "date": {
                "start": "2023-08-09",
                "end": "2023-08-10"
            }
        },
        ...
    },
    ...
}

ここで、startは開始日、endは終了日を示しています。この情報を元に、日付をパースします。

3. 日付の表示

取得してパースした日付データを、Next.jsのコンポーネント内で表示します。例として、上記のデータ構造を元に日付を表示する方法を示します。

pages/news/[id].js

const newsItem = {
    id: response.id,
    title: response.properties.Title.title[0].text.content,
    startDate: response.properties.Date.date.start,
    endDate: response.properties.Date.date.end,
};
return (
    <div>
        <h1>{item.title}</h1>
        <p>{startDate} - {endDate}</p>
        {/* 他の情報を表示 */}
    </div>
);

もし、終了日がない場合や、開始日のみを表示したい場合は、適切に条件分岐を行って調整します。

これで、Notionのデータベースから「Date」プロパティを取得し、Next.jsのページ上で表示することができます。


(一覧ページからタイトルをクリックすると詳細ページに遷移し、日付が表示されました!)

指示:[id].js に、 title と紐づけされた詳細文章である block を表示したい

Notion APIを使用して、ページ(page_id)内のすべてのブロックを取得する場合、blocks.children.listエンドポイントを使用する必要があります。これにより、ページの各ブロック(段落、見出し、リストアイテムなど)を取得できます。

次の手順は、[id].js の getServerSideProps 関数内で、特定のページIDに関連付けられたブロックを取得し、それをページで表示する方法を示しています。

1. ブロックの取得

pages/news/[id].js

export async function getServerSideProps(context) {
    const { id } = context.params;

    // ページの詳細情報を取得
    const response = await notion.pages.retrieve({ page_id: id });

    // ページ内のすべてのブロックを取得
    const blocksResponse = await notion.blocks.children.list({ block_id: id });

    const newsItem = {
        id: response.id,
        title: response.properties.Title.title[0].text.content,
        date: response.properties.Date.date.start,
        blocks: blocksResponse.results,
    };

    return {
        props: {
            newsItem
        }
    };
}

2. ブロックの表示

次に、取得したブロックをページ内で表示します。ブロックの種類によって、表示の仕方が異なる場合があるため、ブロックの type に基づいて条件分岐を行うことができます。

pages/news/[id].js

function NewsDetail({ newsItem }) {
    return (
        <div>
            <h1>{newsItem.title}</h1>
            <div>
                {newsItem.blocks.map((block) => {
                    switch (block.type) {
                        case 'paragraph':
                            return <p key={block.id}>{block.paragraph?.rich_text[0]?.text?.content}</p>;
                        case 'heading_1':
                            return <h1 key={block.id}>{block.heading_1?.rich_text[0]?.text?.content}</h1>;
                        case 'heading_2':
                            return <h2 key={block.id}>{block.heading_2?.rich_text[0]?.text?.content}</h2>;
                        case 'bulleted_list_item':
                            return <li key={block.id}>{block.bulleted_list_item?.rich_text[0]?.text?.content}</li>; 
                        // 他のブロックタイプに基づく条件を追加...
                        default:
                            return null;
                    }
                })}
            </div>
        </div>
    );
}

export default NewsDetail;

上記のコードは、Notionのブロックの基本的なタイプを取り扱う例です。実際の使用ケースやNotionデータベースの構造に応じて、必要に応じてコードを調整することができます。


(内容が表示されました!)


(ただ、他のページで利用していたリストタグ liul タグで囲まれていない問題発生。)

指示:リストブロックの一番最初に <ul> タグ、一番最後に </ul> を足したい

リストアイテムを適切に<ul></ul>で囲むには、隣接するリストアイテムを検出してグループ化するロジックが必要です。以下の手順でこれを実現する方法を説明します:

  1. リストアイテムを検出してグループ化するヘルパー関数を作成します。
  2. グループ化されたリストアイテムを表示するロジックを追加します。

1. ヘルパー関数の作成

以下は、連続する bulleted_list_item をグループ化するヘルパー関数の一例です:

pages/news/[id].js

function groupListItems(blocks) {
  const grouped = [];
  let currentGroup = [];

  blocks.forEach((block, index) => {
    if (block.type === 'bulleted_list_item') {
      currentGroup.push(block);
    } else {
      if (currentGroup.length) {
        grouped.push(currentGroup);
        currentGroup = [];
      }
      grouped.push(block);
    }

    if (index === blocks.length - 1 && currentGroup.length) {
      grouped.push(currentGroup);
    }
  });

  return grouped;
}

2. グループ化されたリストアイテムを表示

次に、上記のヘルパー関数を使用して、NewsDetail コンポーネント内でグループ化されたリストアイテムを表示します。

pages/news/[id].js

function NewsDetail({ newsItem }) {
  const groupedBlocks = groupListItems(newsItem.blocks);

  return (
    <div>
      <h1>{newsItem.title}</h1>
      <div>
        {groupedBlocks.map((groupOrBlock, index) => {
          if (Array.isArray(groupOrBlock)) {
            // This is a group of list items
            return (
              <ul key={index}>
                {groupOrBlock.map(block => (
                  <li key={block.id}>
                    {block.bulleted_list_item?.rich_text[0]?.text?.content}
                  </li>
                ))}
              </ul>
            );
          } else {
            // This is a regular block
            const block = groupOrBlock;
            switch (block.type) {
              case 'paragraph':
                return <p key={block.id}>{block.paragraph?.text[0]?.text?.content}</p>;
              case 'heading_1':
                return <h1 key={block.id}>{block.heading_1?.text[0]?.text?.content}</h1>;
              case 'heading_2':
                return <h2 key={block.id}>{block.heading_2?.text[0]?.text?.content}</h2>;
              // ... 他のブロックタイプの場合も同様に
              default:
                return null;
            }
          }
        })}
      </div>
    </div>
  );
}

この方法を使用すると、連続する bulleted_list_item<ul></ul> で適切に囲まれます。


(正しいHTML構造で表示されました!)

無料版のChatGPTだとあっさりしてる

ちなみに、同じChatGPTでも3.5のほうだとこんな感じの返答でした:

Notion APIを使って、Notionに書いた記事をWebサイトとして公開することができます。以下の手順を参考にしてください。

1. Notion APIを有効化する
2. Notionのデータベースを作成する
3. データベースに記事を追加する
4. Webサイトを作成する
5. Notion APIを使用して、記事をWebサイトに埋め込む

あっさりしてますねーw 仕事の効率化を求めてChatGPTを使うなら、有料版を使った方が捗ります。

感想

思い返せばコーディングよりも最初のNotionの作成や設定に時間がかかった気がします。そして何より、具体的な制作物の内容と、使用するフレームワークやプログラミング内容の基本知識がないと使えないと感じました。記事内の「補足:」とあるところは、ChatGPTからの返答にはありませんでしたが、私がこの記事を公開するために追記したものです。私が事前に知識があったり、簡単な説明でも理解できたのでつまづくことなく進められましたが、事前知識がなかったらその都度質問したり調べたりする必要があります。

また、最終ゴールとなるものが一気に作成されるわけではないので、適切な制作手順を理解し、遂行することも大切です。今回は大したエラーは出なかったのですが、エラーの際はその内容を質問して、修正して、確認して…というのをこれまでどおり繰り返す必要もあります。そのため、やはり知識ゼロの状態では使いこなせないでしょう。

それでも時短になるのは明らかなので、今後も積極的に使ってみたいと思います!あまりに長くなりそうだったので今回は割愛しましたが、見た目の調整や他の機能追加も試してみたいですね!みなさんのおすすめの使い方もぜひ教えてください!

シェアする

ニュースレター

Web制作の最新情報やWebクリエイターボックスからのお知らせ、中の人の近況等を定期的にお送りいたします。 ぜひご登録ください!もちろん無料です! :)