HippoBlog
Web開発に関する備忘録や
日々の雑記ブログ
  • #Nextjs
  • 2023年7月9日
ENTRY TITLE

Next.js x Notion で JAMSTACK するときの備忘録

TEXT BY @hippohack@hippohack
TEXT BY @hippohack@hippohack
  • このエントリーをはてなブックマークに追加

NotionをCMS代わりに利用して、Next.js でのサイト構築方法の相談があったので経験がなかったのでひとまず触ってみたときの備忘録です。

基礎的なことにしか触れてはいないと思います。

そもそも Notion を使うのもこれが初めて。

インテグレーションを追加

  • secret_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX なかんじのトークンが発行される

適当なテーブルを作成

  • リンクをコピーから ID を取り出しておく。
    • もしブラウザで開いていればURLにもある

コネクトを追加

さっき追加したインテグレーションを選択する。

記事にあたるノートを書く

記事の内容は AI に書いてもらった。

APIで記事を取得

yarn add @notionhq/client

.env 追加

NOTION_SECRET=インテグレーション追加したときに発行されたトークンを書く
NOTION_DATABASE_ID=さっき取り出したDBのIDを書く

コード

たぶんやらなくていいことまでやってそうだけど。

# pages/news/index.tsx

    import { Client } from "@notionhq/client";
    import { QueryDatabaseResponse } from "@notionhq/client/build/src/api-endpoints";
    import { useEffect, useState } from "react";

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

    export default function NewsList() {
        const [data, setData] = useState<null | QueryDatabaseResponse>(null);

        const get = async () => {
            const res = await notion.databases.query({
                database_id: process.env.NOTION_DATABASE_ID || "",
                filter: {
                    and: [
                        {
                            property: "published",
                            checkbox: {
                                equals: true,
                            },
                        },
                        {
                            property: "category",
                            multi_select: {
                                contains: "news",
                            },
                        },
                    ],
                },
                sorts: [
                    {
                        property: "date",
                        direction: "descending",
                    },
                ],
            });
            console.log({ res });
            setData(res);
        };

        useEffect(() => {
            get().then((res) => {
                console.log({ res });
            });
        }, []);

        return <div>hoge</div>;
    }

→ でこれレンダリングしてみると、CORS でひっかかる。。

調べてみたけど、結果的にはちゃんと Next.js の作法に乗っかれば解決する。

作法にしたがって修正してみる

型は適当にしかつけてません🙇🙇🙇

記事リスト

# pages/news/index.tsx

    import { Client } from "@notionhq/client";

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

    export async function getStaticProps() {
        try {
            const database = await notion.databases.query({
                database_id: process.env.NOTION_DATABASE_ID || "",
                filter: {
                    and: [
                        {
                            property: "published",
                            checkbox: {
                                equals: true,
                            },
                        },
                        {
                            property: "category",
                            multi_select: {
                                contains: "news",
                            },
                        },
                    ],
                },
                sorts: [
                    {
                        property: "date",
                        direction: "descending",
                    },
                ],
            });

            return {
                props: {
                    data: database.results,
                },
            };
        } catch (error) {
            console.log("Error fetching data:", error);

            return {
                props: {
                    data: null,
                },
            };
        }
    }

    export default function NewsList({ data }: { data: Object[] }) {
        console.log({ data });

        return (
            <>
                {data.map((post: any) => (
                    <div key={post.id}>
                        <a href={`/news/${post.id}`}>{post.id}</a>
                    </div>
                ))}
            </>
        );
    }

記事詳細

# pages/news/[id].tsx

    import { Client } from "@notionhq/client";

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

    export const getStaticPaths = async () => {
        const database = await notion.databases.query({
            database_id: process.env.NOTION_DATABASE_ID || "",
            filter: {
                and: [
                    {
                        property: "published",
                        checkbox: {
                            equals: true,
                        },
                    },
                    {
                        property: "category",
                        multi_select: {
                            contains: "news",
                        },
                    },
                ],
            },
            sorts: [
                {
                    property: "date",
                    direction: "descending",
                },
            ],
        });

        const paths = database.results.map((index) => ({
            params: {
                id: index.id,
            },
        }));

        return { paths, fallback: "blocking" };
    };

    export async function getStaticProps({ params }: { params: { id: string } }) {
        try {
            const page = await notion.blocks.children.list({
                block_id: params.id,
            });

            return {
                props: {
                    data: page.results,
                },
            };
        } catch (error) {
            console.log("Error fetching data:", error);

            return {
                props: {
                    data: null,
                },
            };
        }
    }

    export default function NewsPost({ data }: { data: Post }) {
        console.log({ data });

        return (
            <>
                {data.map((block: any, index: number) => {
                    if (block.heading_1) {
                        return (
                            <h1 key={index}>
                                {block.heading_1.rich_text[0].plain_text}
                            </h1>
                        );
                    }
                    if (block.heading_2) {
                        return (
                            <h2 key={index}>
                                {block.heading_2.rich_text[0].plain_text}
                            </h2>
                        );
                    }
                    if (block.paragraph) {
                        return (
                            <p key={index}>
                                {block.paragraph.rich_text[0].plain_text}
                            </p>
                        );
                    }
                })}
            </>
        );
    }

というかんじで。表示できた。

HTMLにするところはもっといいやり方がありそう。

参考


最後までお読みいただき、ありがとうございました。

ご意見などありましたら@hippohackへDMをお願いいたします。

  • このエントリーをはてなブックマークに追加