logo
コーディング

更新日

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

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

ChatGPT を使って ToDo アプリを作りながらフレームワークを学んでみた

完成形

お知らせ一覧ページにタイトルを表示し、クリックしたら詳細ページに飛ぶというシンプルなもの。制作時間は 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 からの返答にはありませんでしたが、私がこの記事を公開するために追記したものです。私が事前に知識があったり、簡単な説明でも理解できたのでつまづくことなく進められましたが、事前知識がなかったらその都度質問したり調べたりする必要があります。

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

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