# 使用 Notion 作為 Headless CMS 客製 Portfolio 2024.01 huhu --- ## AGENDA 1. 頁面 DEMO 2. 原始需求 3. 實作步驟 (Notion) 4. 延伸問題 5. 應用發想 --- <video data-autoplay src="https://i.imgur.com/oTtniUt.mp4"></video> - 列表可篩選 Tag - 內頁可開啟 Dark Mode - 內頁呈現 Editor 內容 & 客製區塊 - 內頁連結自動指向下一個專案 --- ## 是這樣開始的 ☄️ 設計師捧朋:可以幫我的作品網站做個後台嗎 我連結常常忘記改,求求 🙏 <p><!-- .element: class="fragment" --> 能方便更新作品<br> 最好只要新增內容頁各選單連結自己就會改好<br> (至少選單是動態的)<br> 🙏 </p> <span><!-- .element: class="fragment" -->...</span> ---- ## 📝 - 已有一個 HTML 靜態網站 = **不想重做** - 他具備 HTML / CSS 能力 = **想要自己控制頁面** - 我不會自己建資料庫 = **需要想解決方案** ---- ## 方案選擇 - Json 🤯 ❌ - WordPress ⏳ 💸 ❌ - Notion Database 💡 👀 ✨ ✅ --- ## 實作步驟 1. Notion 建立 Database 2. Notion integration 設定 (拿到 `Token`、`DatabaseID` ) 🔑 3. 串接 Notion API ⛏️👩‍💻 --- STEP 1 ## Notion 建立 Database ---- ## 新增 Database ![add database](https://hackmd.io/_uploads/rJi6hgLcT.png) ---- ## 建立 property 屬性欄位 ![image](https://hackmd.io/_uploads/ryCCJbU56.png) ![table view](https://hackmd.io/_uploads/r1vEixLqp.png) --- STEP 2 ## Notion Integration 設定 建立 Intergration (權限設定) 並綁定到 Database 🚩拿到 `Token` 、 `DatabaseID` <br> [官方文件](https://developers.notion.com/docs/create-a-notion-integration) 很清楚 👍 ---- ## 建立 Intergration [My integrations](https://www.notion.so/my-integrations) > + New intergration ![image](https://hackmd.io/_uploads/ryA5XF9Fp.png =600x) ---- 選擇 workspace / 填 Name ![image](https://hackmd.io/_uploads/ryp_4mTFT.png =600x) ---- 🚩 拿到 `Token` ![image](https://hackmd.io/_uploads/HyuQU-Uc6.png =600x) ---- 訪問權限設定 ![image](https://hackmd.io/_uploads/HJ9GYbU9a.png =600x) ---- ## 綁定 Intergration 到 Database 右上 ... > + Add connections ![image](https://hackmd.io/_uploads/rkNsObIqT.png) ---- 綁完後可以看到訪問權限 ![image](https://hackmd.io/_uploads/rJmzObI96.png) ---- 🚩 拿到 `DatabaseID` 右上 Share > Copy link ![image](https://hackmd.io/_uploads/Hk9qtW8ca.png) ```shell https://www.notion.so/{workspace_name}/{database_id}?v={view_id} ^^^^^^^^^^^^^ ``` --- STEP 3 ## 串接 Notion API Database - 難度 ⭐️ <small>...[官網](https://developers.notion.com/reference/intro)上有更多種類介紹</small> ---- ## 🔧 [JavaScript SDK](https://github.com/makenotion/notion-sdk-js) opensource ```javascript [|1|3|6-7|9|25] const { Client } = require('@notionhq/client'); const notion = new Client({ auth: process.env.NOTION_API_KEY }); (async () => { const databaseId = 'd9824bdc-8445-4327-be8b-5b47500af6ce'; const response = await notion.databases.query({ database_id: databaseId, filter: { or: [ { property: 'In stock', checkbox: { equals: true, }, }, { property: 'Cost of next trip', number: { greater_than_or_equal_to: 2, }, }, ], }, sorts: [ { property: 'Last ordered', direction: 'ascending', }, ], }); console.log(response); })(); ``` ---- Query a database - Response `properties`、`url` 👀 ```jsonld [|3|24|25-33|74-78|34-47|103] { "object": "list", "results": [ { "object": "page", "id": "35ce428e-9280-4b79-8e41-d3384c658b0f", "created_time": "2024-01-28T12:24:00.000Z", "last_edited_time": "2024-01-29T05:00:00.000Z", "created_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "last_edited_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "cover": null, "icon": null, "parent": { "type": "database_id", "database_id": "5d734c14-2b0f-452a-be46-c7bc9267c85e" }, "archived": false, "properties": { "date": { "id": "CG%3CE", "type": "date", "date": { "start": "2024-01-01", "end": null, "time_zone": null } }, "image": { "id": "SXpa", "type": "files", "files": [ { "name": "original-fd88f4ec44bc2fea8bef78f40c46edb9.png", "type": "file", "file": { "url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/b27f15fd-5247-471c-8625-516aecc78dcc/534caafd-b1ca-4c18-99a5-2c83baad3097/original-fd88f4ec44bc2fea8bef78f40c46edb9.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45HZZMZUHI%2F20240130%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20240130T063330Z&X-Amz-Expires=3600&X-Amz-Signature=cacdc1ff2d6558e7a586aece0edfba4f3c9c80cabdd402c6d330dcf7f21ad4a4&X-Amz-SignedHeaders=host&x-id=GetObject", "expiry_time": "2024-01-30T07:33:30.459Z" } } ] }, "tags": { "id": "UQVh", "type": "multi_select", "multi_select": [ { "id": "4a5ffa10-b1b0-4b12-ac4b-91dd293f3604", "name": "branding", "color": "blue" }, { "id": "fa8efd71-fb68-4639-85dd-7f581059d90b", "name": "uiux design", "color": "pink" } ] }, "url": { "id": "WfDf", "type": "rich_text", "rich_text": [] }, "theme": { "id": "kAGM", "type": "select", "select": null }, "enabled": { "id": "%7CFYI", "type": "checkbox", "checkbox": true }, "title": { "id": "title", "type": "title", "title": [ { "type": "text", "text": { "content": "Abc.app", "link": null }, "annotations": { "bold": false, "italic": false, "strikethrough": false, "underline": false, "code": false, "color": "default" }, "plain_text": "Abc.app", "href": null } ] } }, "url": "https://www.notion.so/Abc-app-35ce428e92804b798e41d3384c658b0f", "public_url": null } ], "next_cursor": null, "has_more": false, "type": "page_or_database", "page_or_database": {}, "request_id": "7d096629-b8bf-497a-aa02-8e32e81b7ce2" } ``` --- STEP 3.2 💭 ## 串接 Notion API Page > Block Children - 難度 ⭐️⭐️⭐️⭐️ (嘗試使用 Page 當 Editor) ---- ![image](https://hackmd.io/_uploads/ry2EgN856.png) ---- 官方範例 ```javascript= [|5-10|12-20] const { Client } = require('@notionhq/client'); const notion = new Client({ auth: process.env.NOTION_API_KEY }); // Retrieve a page (async () => { const pageId = '59833787-2cf9-4fdf-8782-e53db20768a5'; const response = await notion.pages.retrieve({ page_id: pageId }); console.log(response); })(); // Retrieve block children (async () => { const blockId = '59833787-2cf9-4fdf-8782-e53db20768a5'; const response = await notion.blocks.children.list({ block_id: blockId, page_size: 50, }); console.log(response); })(); ``` ---- Retrieve a page - Response `properties` 👀 ```json= [|21] { "object": "page", "id": "00cb99e8-3516-40d9-ab23-60773e070f0b", "created_time": "2024-01-21T11:24:00.000Z", "last_edited_time": "2024-01-30T08:07:00.000Z", "created_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "last_edited_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "cover": null, "icon": null, "parent": { "type": "database_id", "database_id": "5d734c14-2b0f-452a-be46-c7bc9267c85e" }, "archived": false, "properties": { "date": { "id": "CG%3CE", "type": "date", "date": { "start": "2023-07-01", "end": null, "time_zone": null } }, "image": { "id": "SXpa", "type": "files", "files": [ { "name": "original-6ec18eb4cb45553bfabece9055d8c917.png", "type": "file", "file": { "url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/b27f15fd-5247-471c-8625-516aecc78dcc/46845705-3a00-4b77-bd91-e3a61db5987b/original-6ec18eb4cb45553bfabece9055d8c917.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45HZZMZUHI%2F20240130%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20240130T084921Z&X-Amz-Expires=3600&X-Amz-Signature=7826e424acbb1f1eb7560b1454efa12c5827fbdd6a1f68e7ff3f21e65717b0d8&X-Amz-SignedHeaders=host&x-id=GetObject", "expiry_time": "2024-01-30T09:49:21.107Z" } }, { "name": "wp6601742-fantastic-mr-fox-wallpapers.jpg", "type": "file", "file": { "url": "https://prod-files-secure.s3.us-west-2.amazonaws.com/b27f15fd-5247-471c-8625-516aecc78dcc/44efd64c-5bd4-4744-91ae-633c6cbb4326/wp6601742-fantastic-mr-fox-wallpapers.jpg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45HZZMZUHI%2F20240130%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20240130T084921Z&X-Amz-Expires=3600&X-Amz-Signature=9b7d6a830abd6c7e48d2e526c97cf5978470b8a0eb2623fb71f674146e87777f&X-Amz-SignedHeaders=host&x-id=GetObject", "expiry_time": "2024-01-30T09:49:21.123Z" } } ] }, "tags": { "id": "UQVh", "type": "multi_select", "multi_select": [ { "id": "b3907d90-8bea-4add-9028-44e626d05c4e", "name": "web design", "color": "purple" }, { "id": "fa8efd71-fb68-4639-85dd-7f581059d90b", "name": "uiux design", "color": "pink" } ] }, "url": { "id": "WfDf", "type": "rich_text", "rich_text": [] }, "theme": { "id": "kAGM", "type": "select", "select": null }, "enabled": { "id": "%7CFYI", "type": "checkbox", "checkbox": true }, "title": { "id": "title", "type": "title", "title": [ { "type": "text", "text": { "content": "B2B ", "link": null }, "annotations": { "bold": false, "italic": false, "strikethrough": false, "underline": false, "code": false, "color": "default" }, "plain_text": "B2B ", "href": null }, { "type": "text", "text": { "content": "Dashboard", "link": null }, "annotations": { "bold": true, "italic": false, "strikethrough": false, "underline": false, "code": false, "color": "default" }, "plain_text": "Dashboard", "href": null } ] } }, "url": "https://www.notion.so/B2B-Dashboard-00cb99e8351640d9ab2360773e070f0b", "public_url": null, "request_id": "12389a96-f279-4434-b972-a35e9a239627" } ``` ---- Retrieve block children - Response `type`、`has_children`、`id` 👀 ```json= [3|23|21|6|67|69|84|117] { "object": "list", "results": [ { "object": "block", "id": "1ba736e5-7c07-4fe4-998e-a1b1ae8f8629", "parent": { "type": "page_id", "page_id": "00cb99e8-3516-40d9-ab23-60773e070f0b" }, "created_time": "2024-01-29T03:59:00.000Z", "last_edited_time": "2024-01-29T03:59:00.000Z", "created_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "last_edited_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "has_children": true, "archived": false, "type": "column_list", "column_list": {} }, { "object": "block", "id": "17df45d0-dce9-49aa-80b3-50614ba211b6", "parent": { "type": "page_id", "page_id": "00cb99e8-3516-40d9-ab23-60773e070f0b" }, "created_time": "2024-01-29T12:21:00.000Z", "last_edited_time": "2024-01-29T12:21:00.000Z", "created_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "last_edited_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "has_children": true, "archived": false, "type": "column_list", "column_list": {} }, { "object": "block", "id": "cf507c16-71a4-49c2-b6c8-175c34c421e6", "parent": { "type": "page_id", "page_id": "00cb99e8-3516-40d9-ab23-60773e070f0b" }, "created_time": "2024-01-29T04:00:00.000Z", "last_edited_time": "2024-01-29T13:09:00.000Z", "created_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "last_edited_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "has_children": true, "archived": false, "type": "callout", "callout": { "rich_text": [ { "type": "text", "text": { "content": "editor__imgbox1", "link": null }, "annotations": { "bold": false, "italic": false, "strikethrough": false, "underline": false, "code": false, "color": "default" }, "plain_text": "editor__imgbox1", "href": null } ], "icon": { "type": "external", "external": { "url": "https://www.notion.so/icons/die1_gray.svg" } }, "color": "gray_background" } }, { "object": "block", "id": "82e0d066-8c52-48b7-8bd3-3a32e115616a", "parent": { "type": "page_id", "page_id": "00cb99e8-3516-40d9-ab23-60773e070f0b" }, "created_time": "2024-01-29T06:33:00.000Z", "last_edited_time": "2024-01-29T06:33:00.000Z", "created_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "last_edited_by": { "object": "user", "id": "a89a2abe-0d2b-47e7-a993-8bd35a07a79b" }, "has_children": false, "archived": false, "type": "paragraph", "paragraph": { "rich_text": [], "color": "default" } } ], "next_cursor": null, "has_more": false, "type": "block", "block": {}, "request_id": "fd87e87c-9c4b-4a9c-b2c3-f7414916b47d" } ``` --- ## 🪄 功能補充 ---- ### 利用 Callout Block 客製頁面元素 ✨ 抓取 callout 的 text 並設為 `<div>` 的 `class` ![callout](https://hackmd.io/_uploads/BJ9BRrLcT.png) ---- ### 利用 files 陣列屬性達成 RWD 換圖 ✨ 因 files 可以上傳多張並可修改順序 可以列規則 ex: 有第二張時第二張則為 mobile 顯示 ![image](https://hackmd.io/_uploads/r1Zl0vU5a.png) --- ## 延伸問題 🧐 - 速度問題,Block 越多越慢 - 待尋找解決方式 ⏳ - 備份 - [Automating Backups with Notion's API](https://notionbackups.com/guides/automated-notion-backup-api#store-data) - Schema size [官方建議](https://developers.notion.com/docs/working-with-databases) 最大 50KB - 非自用 Intergretion 設定流程 ⏳ --- ## 線上應用 - [Super](https://super.so/) 🤑 - No Code 架站 [數位時代介紹文](https://www.bnext.com.tw/article/77375/-nocode-web-super?) - [NotionNext](https://github.com/tangly1024/NotionNext) - 中文開源專案,還有[精美文件](https://docs.tangly1024.com/about) - [Nocode Scripts](https://www.nocode-scripts.com/notion-api-html) 🤑 - [Notion Blocks to html](https://blocks-to-html.com/) --- ## 其他發想 💡 - 活動上傳照片、留言 - 視覺化資料 - 小型商案/個人案 --- ## Reference [Notion 官方文件](https://developers.notion.com/docs/getting-started) [Youtube 教學](https://youtu.be/h6AO-WYB_4c?feature=shared)
{"title":"使用 Notion 作為 Headless CMS 客製 Portfolio","slideOptions":"{\"transition\":\"slide\"}","description":"image.png","contributors":"[{\"id\":\"8b5887e6-2379-4766-a40e-2b7874e93ab1\",\"add\":54015,\"del\":35231}]"}
    161 views