Notion 插件开发入门

news2024/9/25 7:18:08

Notion 插件开发入门

最近想要用 Notion 记笔记,奈何 Notion 的标签分类功能确实不太好用…… 看了看其它文章中配置多级标签的繁杂流程之后,我觉得还是写一个插件比较靠谱……

本文主要介绍 Notion 简单的插件开发,编程语言使用 JavaScript,所以熟悉 JS 的同学应该很快就能上手。官网链接:https://developers.notion.com/docs/getting-started,英语好的同学自取。

1. Notion 数据库

Notion 的灵魂在于数据库,它是 Notion 中最强大也最复杂的功能。下面简单梳理一下 Notion 数据库会用到的知识,其它内容估计自己试试也能摸索出来。数据库官方文档:https://www.notion.so/zh-cn/help/category/databases。

1.1 数据库是什么

简单来说,数据库是一个包含了多个 Notion 页面的集合。数据库可以以多种视图动态展现数据库下的页面:

image-20240814081835562

我们一般常用表格(Table)、看板(Board)、列表(List)三种视图,这里方便起见先用列表演示。列表中展示了数据库下所有的页面,初始化几个页面:

image-20240814082238905

把它们在表格视图下展示就是这个样子。同样地,你也可以在表格中的“名称”一栏新建数据库页面。

image-20240814082404908

1.2 页面属性

数据库中的页面与普通页面不一样,它具有属性(Property)。例如上图的标签、日期就都是属性。常用的属性类型有文本、单选、多选、状态等。

给页面添加属性可以在“表格”视图添加,也可以在页面内部添加:

image-20240814082940157

image-20240814083012668

(可能有些反直觉,但标签确实是用“多选”类型实现的)

那如何新建属性呢?点击表格视图下标题栏的“+”号,选择一个类型:

image-20240814083407514

再根据提示操作即可。

image-20240814083516614

简单布置一下,高级文章管理面板的感觉是不是出来了?

image-20240814083746202

image-20240814083837172

2. 创建你的第一个插件

终于进入正题了。这里将创建第一个 Notion 插件。全程只使用了一个 Notion API:数据库读取。

2.1 先决条件

一定的准备条件(虽然大部分同学可能已经拥有)

  • Node.js 及 NPM
  • 一个 Notion 账号及默认工作空间
  • 一个趁手的 IDE,这里以 VS Code 演示

2.2 在 Notion 中创建插件

(Notion 中,我们又将插件(Plug-in)称为集成(Integration))

打开集成页面,创建新集成;

image-20240814095155373

填写基本信息(集成分为自用的内部集成和公共集成,免费版用户只能创建内部集成):

image-20240814095304130

保存之后你会得到一个集成密钥。复制备用。

image-20240814095550340

2.3 准备 Node.js 环境

用 VS Code 打开一个文件夹,输入以下内容初始化并安装依赖:

npm init
npm install @notionhq/client

然后新建文件 index.js;

新建一个数据库。你可以自己添加内容,也可以复制官方模拟数据。然后在页面中复制 Database ID(如下图):

image-20240814101440079

在数据库页面授权你的插件访问,不然请求会被拒绝:

image-20240814104019011

然后就是 index.js 了~

打开 index.js,引入 Client:

const { Client } = require("@notionhq/client");

定义几个常量:

const NOTION_KEY = "xxx"; // 此处填写你的集成密钥
const NOTION_DATABASE_ID = "xxx"; // 此处填写 Database ID

创建主函数(异步),并在结尾调用。创建新的 Client:

async function main() {
  const notion = new Client({ auth: NOTION_KEY });
}

main();

读取数据库:

const response = await notion.databases.retrieve({ database_id: NOTION_DATABASE_ID }); ;console.log(response);

全貌:

const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx"; // 此处填写你的集成密钥
const NOTION_DATABASE_ID = "xxx"; // 此处填写 Database ID

async function main() {
  const notion = new Client({ auth: NOTION_KEY });
  const response = await notion.databases.retrieve({ database_id: NOTION_DATABASE_ID });	
  console.log(response);
}

main();

运行:

node index.js

成功!

image-20240814104321778

3. Notion 页面 API

插件与 Notion 的交互主要依靠 API(即前面提到的 Client)。这里简单介绍一下 Notion 的页面 API。

官方文档:https://developers.notion.com/reference/post-page

3.1 读取页面信息

做实验肯定需要原材料。所以打开数据库中的一个页面,随便写点什么:

image-20240814110035953

刚刚的数据库有 Database ID,那么页面肯定有 Page ID。复制备用:

image-20240814110409957

读取页面信息的 API 是 notion.pages.retrieve({page_id: string}),用上面的 Page ID 尝试一下:

const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx";
const NOTION_PAGE_ID = "xxx"; // 刚刚复制的 Page ID

async function main() {
  const notion = new Client({ auth: NOTION_KEY });
  const response = await notion.pages.retrieve({ page_id: NOTION_PAGE_ID });
  console.log(response);
}

main();

看看返回了什么:

{
  object: 'page',
  id: 'your_page_id',
  created_time: '2024-08-14T00:13:00.000Z',
  last_edited_time: '2024-08-14T03:00:00.000Z',
  created_by: { object: 'user', id: '4d41673d-b5c2-48eb-b9bc-ab8171bb1aae' },
  last_edited_by: { object: 'user', id: '4d41673d-b5c2-48eb-b9bc-ab8171bb1aae' },
  cover: null,
  icon: null,
  parent: {
    type: 'database_id',
    database_id: 'xxx'
  },
  archived: false,
  in_trash: false,
  properties: {
    '重要性': { id: 'YqII', type: 'select', select: [Object] },
    '状态': { id: 'eM%7B%7B', type: 'status', status: [Object] },
    '标签': { id: 'n%7Dss', type: 'multi_select', multi_select: [Array] },
    '名称': { id: 'title', type: 'title', title: [Array] }
  },
  url: 'https://www.notion.so/Article-1-your_page_id',
  public_url: null,
  request_id: '25d4c385-3100-4c19-bc34-27aeebb6e5d8'
}

再看看 Property,挑一个重要性来获取吧:

const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx";
const NOTION_PAGE_ID = "xxx";

async function main() {
  const notion = new Client({ auth: NOTION_KEY });
  const response = await notion.pages.retrieve({ page_id: NOTION_PAGE_ID });
  console.log(response.properties.重要性.select);
}

main();

返回正常:

{ id: 'utS@', name: 'Very Important', color: 'red' }

3.2 读取页面信息

如果你们有看一些 Notion 的使用教学视频,就一定会听到一句话:在 Notion 中,万物皆为块。页面亦然。所以如果我们要读取页面内容,就要使用页面的另一层身份:块。

有关块信息的官方文档:https://developers.notion.com/reference/patch-block-children

块信息获取的 API:notion.blocks.retrieve({ block_id: string }),其中 Block ID 就是块的 ID。而页面的 Block ID 就是 Page ID。

尝试一下:

const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx";
const NOTION_PAGE_ID = "xxx";

async function main() {
  const notion = new Client({ auth: NOTION_KEY });
  const response = await notion.blocks.retrieve({ block_id: NOTION_PAGE_ID });
  console.log(response);
}

main();

返回:

{
  object: 'block',
  id: 'your_page_id',
  parent: {
    type: 'database_id',
    database_id: 'xxx'
  },
  created_time: '2024-08-14T00:13:00.000Z',
  last_edited_time: '2024-08-14T03:00:00.000Z',
  created_by: { object: 'user', id: '4d41673d-b5c2-48eb-b9bc-ab8171bb1aae' },
  last_edited_by: { object: 'user', id: '4d41673d-b5c2-48eb-b9bc-ab8171bb1aae' },
  has_children: true,
  archived: false,
  in_trash: false,
  type: 'child_page',
  child_page: { title: 'Article 1' },
  request_id: 'f3cd2746-ca65-4126-9b37-522b653c9e26'
}

页面内容呢?还有另一个 API:获取子块。notion.blocks.children.list({ block_id: string })

尝试一下:

const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx";
const NOTION_PAGE_ID = "xxx";

async function main() {
  const notion = new Client({ auth: NOTION_KEY });
  const response = await notion.blocks.children.list({ block_id: NOTION_PAGE_ID });
  console.log(response);
}

main();

拿到子块的 Block ID 了:

{
  object: 'list',
  results: [
    {
      object: 'block',
      id: '88625397-c225-428b-97f4-fee545913d72',
      parent: [Object],
      created_time: '2024-08-14T02:59:00.000Z',
      last_edited_time: '2024-08-14T02:59:00.000Z',
      created_by: [Object],
      last_edited_by: [Object],
      has_children: false,
      archived: false,
      in_trash: false,
      type: 'paragraph',
      paragraph: [Object]
    },
    {
      object: 'block',
      id: '1d83b0c4-4a9f-4fe9-b877-43c5fd5810c5',
      parent: [Object],
      created_time: '2024-08-14T02:59:00.000Z',
      last_edited_time: '2024-08-14T03:00:00.000Z',
      created_by: [Object],
      last_edited_by: [Object],
      has_children: false,
      archived: false,
      in_trash: false,
      type: 'paragraph',
      paragraph: [Object]
    },
    {
      object: 'block',
      id: 'a47674ef-041d-48b5-a696-d23bc4a687b2',
      parent: [Object],
      created_time: '2024-08-14T03:00:00.000Z',
      last_edited_time: '2024-08-14T03:00:00.000Z',
      created_by: [Object],
      last_edited_by: [Object],
      has_children: false,
      archived: false,
      in_trash: false,
      type: 'paragraph',
      paragraph: [Object]
    },
    {
      object: 'block',
      id: 'cab3d9a8-40cd-46ed-9ffa-25b37ac3888e',
      parent: [Object],
      created_time: '2024-08-14T03:00:00.000Z',
      last_edited_time: '2024-08-14T03:00:00.000Z',
      created_by: [Object],
      last_edited_by: [Object],
      has_children: false,
      archived: false,
      in_trash: false,
      type: 'paragraph',
      paragraph: [Object]
    }
  ],
  next_cursor: null,
  has_more: false,
  type: 'block',
  block: {},
  request_id: 'e51de75e-9696-448f-b71a-a86b4b31cabf'
}

再获取一下:

const { Client } = require("@notionhq/client");

const NOTION_KEY = "xxx";
const NOTION_PAGE_ID = "xxx";

async function main() {
  const notion = new Client({ auth: NOTION_KEY });
  const response = await notion.blocks.children.list({
    block_id: NOTION_PAGE_ID,
  });
  const ids = response.results.map((e) => e.id);
  for (const i in ids) {
    const j = await notion.blocks.retrieve({ block_id: ids[i] });
    console.log(j.paragraph.rich_text); // 获取文本
  }
}

main();

Perfect~

[
  {
    type: 'text',
    text: { content: 'Paragraph 1', link: null },
    annotations: {
      bold: false,
      italic: false,
      strikethrough: false,
      underline: false,
      code: false,
      color: 'default'
    },
    plain_text: 'Paragraph 1',
    href: null
  }
]
[
  {
    type: 'text',
    text: { content: 'Paragraph 2', link: null },
    annotations: {
      bold: true,
      italic: false,
      strikethrough: false,
      underline: false,
      code: false,
      color: 'default'
    },
    plain_text: 'Paragraph 2',
    href: null
  }
]
[
  {
    type: 'text',
    text: { content: 'Paragraph 3', link: null },
    annotations: {
      bold: false,
      italic: true,
      strikethrough: false,
      underline: false,
      code: false,
      color: 'default'
    },
    plain_text: 'Paragraph 3',
    href: null
  }
]

3.3 官方文档的阅读

本文不能列举出所有 API 的使用方法,具体详见前面给出的官方文档链接。

官方文档食用方法:以 Create a page 为例。(搭配英语好的大脑或浏览器翻译插件食用更佳)

左栏为说明及参数:

image-20240814123357744

右栏为示例。上方为示例代码(JavaScript 要选上!)

image-20240814123624415

下方为响应类型及响应内容(左边就是参数了)。

image-20240814123752128

4. Notion 数据库 API

简单介绍一下 Notion 的数据库 API。

官方文档:https://developers.notion.com/reference/create-a-database

4.1 读取数据库信息

即上文提到过的第一个 API。

API:notion.databases.retrieve({ database_id: string });

示例就不用了,看上文就行。

4.2 查询数据库信息

API:notion.databases.query({ database_id: string, sort: array, filter: json })

database_id 大家都很熟悉,这里说一下其它两个。

首先是 sort。sort 是一个存储排序规则的数组。排序规则为形似 { property: ‘Name’, direction: ‘ascending’ } 的 JSON。property 为依据排序的属性,direction 为排序方向。ascending 为升序,descending 为降序。

详见 https://developers.notion.com/reference/post-database-query-sort

然后是 filter。filter 即过滤器,可以帮你过滤你不想要的页面。“and”为和,“or”为或。示例:

{
  "and": [
    {
      "property": "Done",
      "checkbox": {
        "equals": true
      }
    }, 
    {
      "or": [
        {
          "property": "Tags",
          "contains": "A"
        },
        {
          "property": "Tags",
          "contains": "B"
        }
      ]
    }
  ]
}

详见 https://developers.notion.com/reference/post-database-query-filter

5. Notion 富文本

Notion 中的文本基于富文本。在 Notion 中,无论是获取信息,还是插入信息,都离不开它。下面简单介绍它的使用。

官方文档:https://developers.notion.com/reference/block,https://developers.notion.com/reference/rich-text

Notion 块的显示取决于两个重要因素,一个是块类型,另一个就是内部富文本。主要的块类型详见官方文档:

image-20240819171241390

如果没有特殊情况,一般的块类型为段落(Paragraph)。通常一个块的内容可以用如下方式定义:

{
  paragraph: { // 可以切换成不同的块类型
    rich_text: [ 
      // 内部的富文本列表……
    ]
  }
}

接着来说富文本。Notion 的富文本分为三类:文本、提及和等式。等式内容少用所以请自行查阅官方文档。

文本类的富文本主要结构如下:

{
  type: "text", // 类型
  text: {
    content: "some text", // 文本内容
    link: null // 文本链接,少用
  },
  annotations: {
    bold: false, // 是否为粗体
    italic: false, // 是否为斜体
    strikethrough: false, // 删除线
    underline: false, // 下划线
    code: false, // 代码风格文字,注意不是代码块
    color: "default" // 颜色
  },
  plain_text: "some text", // 纯文本,便于程序员直接访问无格式文本内容,由 Notion 自动生成
  												 // 如果自己初始化富文本,内容与文本内容一致即可
  href": null // 链接,少用
}

提及即 Notion 中近似于链接的内容,熟悉 Notion 的同学应该清楚。提及中最重要的部分是 ID。主要结构如下(以数据库提及为例):

{
  type: "mention",
  mention: {
    type: "database", // 可以替换为其它内容类别,比如 page
    database: { // 同上
      id: "xxx" // ID
    }
  },
  annotations: {
    bold: false,
    italic: false,
    strikethrough: false,
    underline: false,
    code: false,
    color: "default"
  },
  plain_text: "xxx",
  href: "https://www.notion.so/xxx" // 格式为 https://www.notion.so/ 加上 ID
  																  // Notion 一般会自动生成
}

下面是一个提及块的完整示例:

{
  paragraph: {
    rich_text: [
      {
      type: "mention",
      mention: {
        type: "page",
        page: {
          id: "xxx",
        },
      },
      annotations: {
        bold: false,
        italic: false,
        strikethrough: false,
        underline: false,
        code: false,
        color: "default",
      },
      plain_text: "This is a test page",
      href: "https://www.notion.so/" + id.replace("-", ""),
    },
  ],
},

由于富文本很多内容可以复用,所以建议封装一下:

image-20240819173206806

6. 实战

了解了一些 API 之后,我们就可以进入实战环节了。Notion 插件通常以浏览器插件 + Notion 集成的方式实现(或许也可以用油猴脚本?还没尝试过)。

我的 Notion 插件叫 TagPlus,顾名思义它用来增强 Notion 标签管理功能。它在本地运行得很不错:

image-20240819173500612

image-20240819173444363

最后,Enjoy it~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2053296.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

打工人的“低成本的高生产力”之ToDesk云电脑

在快节奏的现代生活中,是不是经常觉得钱包瘪得快,但工作压力却大得要命?想要提升效率,又不想掏空腰包?来来来,作为职场老油条, 今儿给你们安利个神器——ToDesk云电脑,简直是咱们打工…

子域名太多如何实现HTTPS?一张通配符SSL证书全搞定

在当今数字化时代,网站安全性已经成为网站运营者以及访问者都非常关注的重要问题。部署SSL证书实现HTTPS加密,确保数据传输安全,防止信息被泄露或篡改,消除浏览器“不安全”提示,提高网站安全性以及可信任度已成为必然…

期权应该怎么及时止损?期权止损有哪些方式?

今天带你了解期权应该怎么及时止损?期权止损有哪些方式?如何在期权的交易计划中设置合适的止损点”是相对简单的事情。 如果自己遭遇了一定的损失,就要及时止损。 一般来说如果亏损接近30%的时候就可以进行止损了。但是不同的投资者资金实力…

集合及数据结构第二节————算法、时间复杂度和空间复杂度

系列文章目录 集合及数据结构第二节————算法、时间复杂度和空间复杂度 算法、时间复杂度和空间复杂度 数据结构和算法的关系.算法的定义算法的特性算法设计的要求算法效率时间复杂度的概念大O的渐进表示法常见时间复杂度计算举例常见空间复杂度计算举例 文章目录 系列文…

关于windows环境使用nginx的一些性能问题

遇到的问题 最近在一个windows环境中部署nginx,遇到了以下问题: 1. nginx启动了九个线程(1master8woekr),但是所有链接都被1个woker接收,其余worker不工作 2. 用户端访问web很慢,登录服务器使…

xcode配置使用摄像头和相册权限,没有Info.plist文件也可以配置,解决Thread 4: signal SIGABRT报错问题

最新的Xcode更改了相册和相机的权限关键字,在进行真机调试,或真正在用户使用的时候需要添加这些权限,否则在程序正确时仍然会产生下面的错误: Thread 4: signal SIGABRT This app has crashed because it attempted to access pri…

【Web APIs】JavaScript 操作元素 ④ ( 修改元素属性示例 | 密码表单标签结构 | 密码输入框样式设置 | 右侧图标按钮设置 | JavaScript 修改元素属性示例 )

文章目录 一、案例需求二、关键要点1、密码表单标签结构2、设置盒子样式3、密码输入框样式设置4、右侧图标按钮设置5、盒子模型右侧图标按钮设置 三、JavaScript 修改元素属性示例四、完整代码示例 JavaScript 中 可以通过 DOM ( 文档对象模型 ) 操作 来 修改网页的 内容 , 结构…

牛客JS题(四十六)斐波那契数列

注释很详细&#xff0c;直接上代码 涉及知识点&#xff1a; 递归斐波那契数列 题干&#xff1a; 我的答案 <!DOCTYPE html> <html><head><meta charset"UTF-8" /><style>/* 填写样式 */</style></head><body><!-…

宝马销量崩了,不卷价格就卷铺盖

文 | AUTO芯球 作者 | 雷慢 宝马这回真天塌了&#xff0c; 还记得7月初宝马宣布涨价吗&#xff0c; 我当初就劝我那准备宝马i3的同学说&#xff0c; 别急&#xff0c;怎么涨上去的就会怎么跌回去。 这不&#xff0c;一涨价&#xff0c;价格是保住了&#xff0c;但是销量惨…

【获取本机简要配置信息】(bat)

输出结果(示例)如下 如果提示 ‘系统找不到指定的路径’ 请把set Log那行的路径换一下&#xff0c;换成一个存在的路径就行 比如直接放C盘 set LogC:\本机配置信息.txt 如果提示 “客户端没有所需的特权” 请右键后选择 “以管理员身份运行” 上代码 echo off::设置信息保存路…

【17】暴力递归改dp(下)

目录 一.两人玩游戏 二.象棋游戏 三.鲍勃存活 四.凑钱方案数问题 一.两人玩游戏 题目&#xff1a;有一个正整数数组&#xff0c;A和B两个人轮流拿数组的最左或最右的数值&#xff0c;返回最终的最高分数。 暴力递归版本 public static int win1(int[] arr) {if (arr null…

Chat App 项目之解析(三)

Chat App 项目介绍与解析&#xff08;一&#xff09;-CSDN博客文章浏览阅读76次。Chat App 是一个实时聊天应用程序&#xff0c;旨在为用户提供一个简单、直观的聊天平台。该应用程序不仅支持普通用户的注册和登录&#xff0c;还提供了管理员登录功能&#xff0c;以便管理员可以…

webflux源码解析(1)-主流程

目录 1.关键实例的创建1.1 实例创建1.2 初始化 2.处理请求的关键流程2.1 从ReactorHttpHandlerAdapter开始2.1 DispatcherHandler的初始化2.2查找mapping handler2.3 处理请求(执行handler)2.4 返回结果处理 3.webflux的配置装配参考&#xff1a; WebFlux是Spring 5.0框架推出的…

算法的学习笔记—对称的二叉树(牛客)

&#x1f600;前言 在算法的世界中&#xff0c;二叉树是一个极其重要的数据结构。它不仅广泛应用于各种算法的设计中&#xff0c;也是面试中常见的考察点之一。今天&#xff0c;我们将深入探讨一个经典的二叉树问题——对称的二叉树&#xff0c;并且会展示如何通过Java代码来解…

趋动VAICP技术认证全球考试正式上线

8月18日&#xff0c;趋动科技主办的VAICP(VirtAl Certified Professional) Al算力池化专家认证考试正式在 Pearson VUE上线。 即日起&#xff0c;凡参加过VAICP培训的学员&#xff0c;可立即通过Pearson VUE官网注册参加考试&#xff0c;官网链接&#xff1a;https://home.pea…

是肯定,更是动力 | 一封封感谢信纷至沓来,全视通服务获赞誉

在全视通的故事里&#xff0c;有那么一群人&#xff0c;他们穿梭于全国各地&#xff0c;奔波于各种交付调试、维检。山河湖海&#xff0c;严寒酷暑&#xff0c;从晨曦至夜幕&#xff0c;他们只为兑现那份让客户更加满意的承诺。他们是使命必达的守护者——全视通服务团队。他们…

串口UART

常见通信接口 串口定义 串口定义&#xff1a; 通用串行异步收发器 通用&#xff1a;UART的应用非常广泛&#xff0c;应用领域&#xff1a;工控行业&#xff0c;电力系统等串行&#xff1a;处理器和外设之间只需连接一根信号线&#xff0c;处理器和外设数据传输是一个bit位一…

CORS跨域问题全解:原理、问题与解决方案

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

【Qt】QDesigner 源码 控件图片资源路径

qttools-5.12.12-designer-1\qttools-5.12.12-designer\qttools-5.12.12\src\designer\src\components\formeditor\images\win qttools-5.12.12-designer-1\qttools-5.12.12-designer\qttools-5.12.12\src\designer\src\components\formeditor\images\widgets

docker部署drawio

1&#xff09;介绍Drawio.io GitHub&#xff1a;GitHub - jgraph/drawio: draw.io is a JavaScript, client-side editor for general diagramming. Draw.io是一款开源的绘制流程图的工具&#xff0c;拥有大量免费素材和模板。程序本身支持中文在内的多国语言&#xff0c;创建…