Notion APIと11tyではじめるヘッドレスCMSサイト制作

Notion APIと11tyではじめるヘッドレスCMSサイト制作

Notion APIと静的サイトジェネレーター11ty(Eleventy)を使ってAPIによるデータ取得とHTMLの生成を行います。
Clock Icon2025.04.07

この記事では、NotionをヘッドレスCMSとして利用してHTMLコンテンツを作成する手順を紹介します。フレームワークはHTMLコーダーと相性の良い11tyを使って、最小限の構成で構築してみます。

対象ユーザーのスキル

  • HTML
  • CSS
  • JS
  • GulpやWebpack等のビルドツール利用経験

11tyとは

11tyは複数のテンプレート形式に対応している静的サイトジェネレーターで、HTMLを出力することができます。Markdown(.md)やEJS(.ejs)、Nunjucks(.njk)といった複数のテンプレート形式に対応しているほか、APIからのデータ取得、JSONデータの読み込み等も行えます。今回は11tyのなかでもドキュメントが充実しているNunjucksを使用します。

Notion APIの準備

Notionページを作成する

Notionページを作成して、APIでデータを取得するためのデータベースを用意します。
イベント告知ページを作成する想定で「メンバー」と「スケジュール」のテーブルビューを作成しています。
img_notion-api-11ty-cms01
カラム名と種類は後ほどデータを取得する際に利用します。

  • メンバー
    • 「Name」 種類:タイトル
    • 「image」 種類:ファイル&メディア
  • スケジュール
    • 「title」 種類:タイトル
    • 「date」 種類:日付

左サイドナビから設定、続けて、接続 - インテグレーションを作成または管理するをクリック。
img_notion-api-11ty-cms02

新しいインテグレーションを作成をクリック
img_notion-api-11ty-cms03

インテグレーション名を入力、関連ワークスペースを選択します。種類は、ここでは「内部」にしておきます。
img_notion-api-11ty-cms04

シークレットキーが発行されます。
用途に合わせて必要な権限を設定します。ここでは「コンテンツを読み取る」のみチェックを入れて保存をクリック。
img_notion-api-11ty-cms05

作成したインテグレーションをNotionページに紐づけます。
接続から作成したインテグレーションを選択。
img_notion-api-11ty-cms08

親ページに設定しておけば、子ページである各テーブルへの接続設定は不要になるので、この設定にしておきます。
img_notion-api-11ty-cms09

これでNotion APIの下準備ができました。
APIを叩いてデータを取得できることを確認してみます。ここでは「メンバー」のデータベースを指定してみます。
参考:Notion公式APIリファレンス Query a database

curl -X POST 'https://api.notion.com/v1/databases/XXXXXXXXXX/query' \
-H 'Notion-Version: 2022-06-28' \
-H 'Authorization: Bearer ntn_XXXXXXXXXX' 
  • 1行目のXXXXXXXXXXにはデータベースIDを入れます。メンバーのテーブルビューをフルページで開いてURLから32文字の英数字の文字列を取得します。
    img_notion-api-11ty-cms07
    参考:Notion公式APIリファレンス データベースを取得する

  • 3行目のntn_XXXXXXXXXXにはインテグレーション編集ページの内部インテグレーションシークレットを入れます。

JSONが返ってきました。全部載せると長いので、こちらはPrettifyにかけてから抜粋したものになります。

{
  "object": "list",
  "results": [
    {
      "properties": {
        "image": {
          "id": "%3B%5DQi",
          "type": "files",
          "files": [
            {
              "name": "img_nekotaro.png",
              "file": {
                "url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/**********/img_nekotaro.png",
                "expiry_time": "2025-04-05T14:08:53.288Z"
              }
            }
          ]
        },
        "Name": {
          "id": "title",
          "type": "title",
          "title": [
            {
              "type": "text",
              "plain_text": "ねこたろう",
              "href": null
            }
          ]
        }
      }
    }
  ]
}

これでNotion APIのデータをJSON形式で取得できることが確認できました。
続いて、このJSONを埋め込んだHTMLを生成していきます。

ビルド環境の準備

検証時の環境

  • macOS Sonoma バージョン: 14.6.1
  • node: v22.11.0
  • Google Chrome バージョン: 135.0.7049.41

ホームディレクトリに検証用ディレクトリを作成します。

cd ~
mkdir notion-api-11ty

検証用ディレクトリに移動します。

cd notion-api-11ty

package.jsonを生成し、ローカルに11tyをインストールします。

npm init -y
npm install --save-dev @11ty/eleventy

package.jsonの"scripts"に "build": "eleventy" を追記します。

package.json
{
  "name": "11ty_minimum",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
-   "test": "echo \"Error: no test specified\" && exit 1"
+   "test": "echo \"Error: no test specified\" && exit 1",
+   "build": "eleventy"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@11ty/eleventy": "^2.0.1"
  }
}

API-KEYの設定 / データの取得

Notion APIにアクセスしてデータをとってくるためにHTTPリクエストのライブラリが必要なのでnode-fetchを使います。またAPIキーのベタ書きを避けたいので、環境変数ファイルを利用します。

プロジェクトのルートディレクトリに.envファイルを作成して、Notionから発行されるAPIキーを設定します。

touch .env
.env
//Notin APIキー
API_KEY = ntn_**********************************************

//Notion データベースID
DATABASE_MEMBERS_ID = *********************************
DATABASE_SCHEDULES_ID = *********************************

_dataディレクトリの中にjsファイルを入れるとグローバルデータファイルとして(プロジェクト全体で)使用できるようになります。
参考サイト:https://www.11ty.dev/docs/data-js/

mkdir _data
touch ./_data/api_members.js
touch ./_data/api_schedules.js
api_members.js
const Client = require('@notionhq/client').Client;
require('dotenv').config();
const notion = new Client({ auth: process.env.API_KEY });

module.exports = async function() {
  const articleDataResponse = await notion.databases.query({
    database_id: process.env.DATABASE_MEMBERS_ID,
    sorts: [
      {
        property: 'Name',
        direction: 'ascending',
      },
    ],
  });
  return articleDataResponse.results;
};
api_schedules.js
const Client = require('@notionhq/client').Client;
require('dotenv').config();
const notion = new Client({ auth: process.env.API_KEY });

module.exports = async function() {
  const scheduleDataResponse = await notion.databases.query({
    database_id: process.env.DATABASE_SCHEDULES_ID,
    sorts: [
      {
        property: 'date',
        direction: 'ascending',
      },
    ],
  });
  return scheduleDataResponse.results;
};

dotenvと@notionhq/clientをインストールして上記のファイルを使えるようにします。

npm install --save-dev dotenv
npm install --save-dev @notionhq/client

package.jsonにも追記されました。

HTMLテンプレート作成

HTMLテンプレートを用意します。

touch index.njk
index.njk
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Notion-APIと11tyで作るサイト</title>
</head>

<body>
  <h1>Notion-APIと11tyで作るサイト</h1>

  <section>
    <div>
      <h2>メンバー</h2>
      <ul>
        {%- for member in api_members %}
        <li>
          <p>なまえ:{{member.properties.Name.title[0].plain_text}}</p>
          <img src="{{member.properties.image.files[0].file.url}}" style="width:50px;">
        </li>
        {% endfor %}
      </ul>
    </div>
  </section>

  <section>
    <div>
      <h2>スケジュール</h2>
      <ul>
        {% for schedule in api_schedules %}
        <li>
          <p>イベント:{{schedule.properties.title.title[0].plain_text}}</p>
          <p>日にち:{{schedule.properties.date.date.start}}</p>
        </li>
        {% endfor %}
      </ul>
    </div>
  </section>
</body>
</html>
npm run build

HTMLファイルが生成されました。 ビルドの出力先はデフォルトだと_siteディレクトリになります。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Notion-APIと11tyで作るサイト</title>
</head>

<body>
  <h1>Notion-APIと11tyで作るサイト</h1>

  <section>
    <div class="inner">
      <h2 class="collection__ttl">メンバー</h2>
      <ul class="new-collections">
        <li>
          <p>なまえ:ちえぞう</p>
          <img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/**********/img_chiezo.png" style="width:50px;">
        </li>

        <li>
          <p>なまえ:ねこぞう</p>
          <img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/**********/img_nekozo.png" style="width:50px;">
        </li>

        <li>
          <p>なまえ:ねこたろう</p>
          <img src="https://prod-files-secure.s3.us-west-2.amazonaws.com/**********/img_nekotaro.png" style="width:50px;">
        </li>

      </ul>
    </div>
  </section>

  <section>
    <div class="inner">
      <h2 class="collection__ttl">スケジュール</h2>
      <ul class="new-collections">

        <li>
          <p>イベント:ふりかえり勉強会</p>
          <p>日にち:2024-12-10</p>
        </li>

        <li>
          <p>イベント:ビジネス活用にむけた最新動向キャッチアップ</p>
          <p>日にち:2024-12-11</p>
        </li>

        <li>
          <p>イベント:re:cap</p>
          <p>日にち:2024-12-12</p>
        </li>

      </ul>
    </div>
  </section>
</body>
</html>

実際の画像のURLは、S3 Presigned URLになっています。期限があるので公開する際は別途対応が必要になります。

img_notion-api-11ty-cms06

ブラウザでの表示も確認できました。
チームにHTMLを触らないメンバーがいても、Notionを編集してもらうことで更新ができるようになります。
クラスメソッドのイベントサイトは会期中に何度も更新が入ることがあるので、そういった更新頻度の高いページを制作する際に重宝しそうです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.