Notion APIを使ってデータベースにアクセスして、ウェブサイトを表示する方法が、WordPressにとってかわるという内容の話をXの投稿で見かけました。
投稿ではBolt.newを使って作っていましたが、私はBolt.newの無料版のせいか上手くできませんでした。
それでNotion APIでデータベースの内容を得る方法や、記事の内容を得る方法を勉強しようと思いました。
今回は、備忘録の意味も込めて記事にしました。
コードは、GPT-4oを使って制作しました。
このコードで、Notion APIの動作確認ができます。
データベースから記事IDを得て記事の内容を得る
必要なパラメーター
こちらのページで、インテグレーションの作成をします。
https://www.notion.so/profile/integrations
データベースのIDの確認方法
下記の記事などを参考されると、分かりやすいです。
https://note.com/amatyrain/n/ncf7111f74ffa
コマンド
npmを使います。
Notion API SDKを使います。
#Notion API SDK for JavaScript
npm install @notionhq/client
特にフレームワークを使っていませんので、index.jsなどのファイル名で実行できます。
node index.js
const { Client } = require("@notionhq/client");
const fs = require("fs");
//インテグレーション
const notion = new Client({
auth: "******************************",
});
//データベースID
const DATABASE_ID = "*****************";
// ページの本文(ブロック内容)を取得する関数(再帰的に子ブロックも取得)
async function getPageContent(pageId) {
const response = await notion.blocks.children.list({
block_id: pageId,
});
console.log("Fetching content for pageId:", pageId); // デバッグ用ログ
console.log("Response:", response); // APIレスポンスを確認
// 各ブロックが子ブロックを持つ場合、再帰的にその子ブロックも取得
for (let block of response.results) {
if (block.has_children) {
block.children = await getPageContent(block.id);
}
}
return response.results;
}
// ブロックからテキストを取得する関数(再帰的に子ブロックも処理)
function getTextFromBlock(block) {
let textContent = "";
console.log(`Processing block type: ${block.type}`); // ブロックタイプのログ出力
// 各ブロックタイプに応じて `rich_text` フィールドから内容を抽出
switch (block.type) {
case "paragraph":
textContent = block.paragraph.rich_text.map(textItem => textItem.plain_text).join("");
break;
case "heading_1":
textContent = `<h1>${block.heading_1.rich_text.map(textItem => textItem.plain_text).join("")}</h1>`;
break;
case "heading_2":
textContent = `<h2>${block.heading_2.rich_text.map(textItem => textItem.plain_text).join("")}</h2>`;
break;
case "heading_3":
textContent = `<h3>${block.heading_3.rich_text.map(textItem => textItem.plain_text).join("")}</h3>`;
break;
case "to_do":
const checked = block.to_do.checked ? "[x]" : "[ ]";
textContent = `${checked} ${block.to_do.rich_text.map(textItem => textItem.plain_text).join("")}`;
break;
case "bulleted_list_item":
textContent = `• ${block.bulleted_list_item.rich_text.map(textItem => textItem.plain_text).join("")}`;
break;
case "numbered_list_item":
textContent = `${block.numbered_list_item.rich_text.map(textItem => textItem.plain_text).join("")}`;
break;
case "toggle":
textContent = `<details><summary>${block.toggle.rich_text.map(textItem => textItem.plain_text).join("")}</summary>`;
break;
case "quote":
textContent = `<blockquote>${block.quote.rich_text.map(textItem => textItem.plain_text).join("")}</blockquote>`;
break;
case "code":
const language = block.code.language;
textContent = `<pre><code class="${language}">${block.code.rich_text.map(textItem => textItem.plain_text).join("")}</code></pre>`;
break;
case "callout":
textContent = `<div class="callout">${block.callout.rich_text.map(textItem => textItem.plain_text).join("")}</div>`;
break;
default:
textContent = ""; // 未知のブロックタイプには対応せず、空白に設定
break;
}
console.log(`Extracted text for block type ${block.type}: ${textContent}`); // 抽出されたテキストのログ出力
// 子ブロックがある場合は再帰的に処理
if (block.has_children && block.children) {
textContent += "<ul>";
for (const child of block.children) {
textContent += `<li>${getTextFromBlock(child)}</li>`;
}
textContent += "</ul>";
}
// トグルブロックの場合、閉じタグを追加
if (block.type === "toggle") {
textContent += "</details>";
}
return textContent;
}
// データベースの内容を取得する関数
async function getDatabaseContent() {
try {
const response = await notion.databases.query({
database_id: DATABASE_ID,
});
let htmlContent = `
<html>
<head>
<title>Notion Database Content</title>
<style>
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; border: 1px solid #ccc; }
th { background-color: #f4f4f4; }
</style>
</head>
<body>
<h1>Notionデータベースの内容</h1>
<table>
<tr>
<th>ページID</th>
<th>タイトル</th>
<th>内容</th>
</tr>
`;
for (const page of response.results) {
const pageId = page.id;
const title = page.properties["名前"]?.title?.[0]?.text?.content || "タイトル未設定";
const contentBlocks = await getPageContent(pageId);
let content = contentBlocks.map(block => getTextFromBlock(block)).join("");
htmlContent += `
<tr>
<td>${pageId}</td>
<td>${title}</td>
<td>${content}</td>
</tr>
`;
}
htmlContent += `
</table>
</body>
</html>
`;
fs.writeFileSync("notion_database_content.html", htmlContent);
console.log("HTMLファイル 'notion_database_content.html' が作成されました");
} catch (error) {
console.error('エラーが発生しました:', error);
}
}
getDatabaseContent();
生成されたHTML
************** は、ページIDです。
<html>
<head>
<title>Notion Database Content</title>
<style>
table { width: 100%; border-collapse: collapse; }
th, td { padding: 8px; border: 1px solid #ccc; }
th { background-color: #f4f4f4; }
</style>
</head>
<body>
<h1>Notionデータベースの内容</h1>
<table>
<tr>
<th>ページID</th>
<th>タイトル</th>
<th>内容</th>
</tr>
<tr>
<td>************************************</td>
<td>page1</td>
<td></td>
</tr>
<tr>
<td>************************************</td>
<td>テスト</td>
<td>2024年11月9日</td>
</tr>
<tr>
<td>************************************</td>
<td>page2</td>
<td>1234</td>
</tr>
</table>
</body>
</html>
表示例
主要なブロックの一覧
Paragraph(段落)
内容の取り出し: block.paragraph.rich_textからテキストを取得します。
const text = block.paragraph.rich_text.map(textItem => textItem.plain_text).join("");
Heading 1, 2, 3(見出し1, 2, 3)
内容の取り出し: block.heading_1.rich_text(またはheading_2, heading_3)からテキストを取得します。
const text = block.heading_1.rich_text.map(textItem => textItem.plain_text).join("");
To-do(チェックリスト)
内容の取り出し: block.to_do.rich_textからテキストを取得し、block.to_do.checkedでチェック状態を確認します。
const text = block.to_do.rich_text.map(textItem => textItem.plain_text).join("");
const checked = block.to_do.checked ? "[x]" : "[ ]";
Bulleted List Item(箇条書き)
内容の取り出し: block.bulleted_list_item.rich_textからテキストを取得します。
const text = block.bulleted_list_item.rich_text.map(textItem => textItem.plain_text).join("");
Numbered List Item(番号付きリスト)
内容の取り出し: block.numbered_list_item.rich_textからテキストを取得します。
const text = block.numbered_list_item.rich_text.map(textItem => textItem.plain_text).join("");
Toggle(トグル)
内容の取り出し: block.toggle.rich_textからテキストを取得します。
const text = block.toggle.rich_text.map(textItem => textItem.plain_text).join("");
Quote(引用)
内容の取り出し: block.quote.rich_textからテキストを取得します。
const text = block.quote.rich_text.map(textItem => textItem.plain_text).join("");
Code(コード)
内容の取り出し: block.code.rich_textからテキストを取得し、block.code.languageで言語を確認します。
const text = block.code.rich_text.map(textItem => textItem.plain_text).join("");
const language = block.code.language;
Callout(コールアウト)
内容の取り出し: block.callout.rich_textからテキストを取得します。
const text = block.callout.rich_text.map(textItem => textItem.plain_text).join("");
まとめ
今回は、Notion APIからデータの取り出しができるように、コードを作りました。
今回のコードでページのタイトルやコンテンツのブロックの抽出ができましたので、ウェブサイトの表示ができるように加工も可能と思います。
Bolt.newなどのAIを使ったコーディング支援を使ってコードを書くのも良いのですが、仕組みを理解するためには、こうした方法で作っていくのも良いと思います。