本周我发布了 Datasette 1.0 的第一个 alpha 版本,其中有一个重要的新功能:Datasette 核心现在包含一个 JSON API ,用于创建和删除表以及插入、更新和删除数据。
结合 Datasette 现有的用于读取和过滤表数据以及执行 SELECT 查询的 API,这有效地将 Datasette 转变为适用于任何应用程序的 SQLite 支持的 JSON 数据层。
如果您以正确的方式斜视它,您甚至可以将其描述为为 SQL 数据库提供 NoSQL 接口!
我从事这项工作的最初动机是提供一个 API,用于将数据加载到我的Datasette Cloud SaaS 产品中——但现在我已经开始使用它,我意识到它可以应用于许多有趣的事情。
我在星期三发布了 1.0a0 alpha ,然后在过去的两天里解决了一些错误(在1.0a1中发布)并构建了一些说明性演示。
抓取 Hacker News 以构建原子提要
我的第一个演示重用了我今年早些时候的 scrape-hacker-news-by-domain项目。
https://news.ycombinator.com/from?site=simonwillison.net是 Hacker News 上显示来自我博客的提交的页面。我喜欢关注那个页面,看看是否有人链接到我的作品。
来自该页面的数据目前无法通过官方 Hacker News API获得……但它采用 HTML 格式,非常容易抓取。
我的shot-scraper命令行浏览器自动化工具能够针对网页执行 JavaScript 并将抓取的数据作为 JSON 返回。
我在使用 shot-scraper 从命令行抓取网页中写到了这一点,包括抓取黑客新闻页面的方法,如下所示:
shot-scraper javascript \ “ https://news.ycombinator.com/from?site=simonwillison.net ” \ -i scrape.js -o simonwillison-net.json
这是scrape.js脚本。
几个月来,我一直在运行一个Git scraper ,它使用 GitHub Actions 从我的simonw/scrape-hacker-news-by-domain存储库中执行该抓取脚本。
今天,我修改了该脚本,以使用新的 API 将它抓取的数据发布到我的个人 Datasette Cloud 帐户——然后使用datasette-atom插件从该数据生成 Atom 提要。
这是 Datasette Cloud 中的新表。
这是在 GitHub Actions 中运行并将数据推送到 Datasette 的bash
脚本:
导出SIMONWILLISON_ROWS= $( jq -n --argjson 行“ $( cat simonwillison-net.json ) ” \ ' {“行”:$行,“替换”:真} ' ) 卷曲-X POST \ https://simon.datasette.cloud/data/hacker_news_posts/-/insert \ -H “内容类型:应用程序/json ” \ -H "授权:不记名$DS_TOKEN " \ -d " $SIMONWILLISON_ROWS "
$DS_TOKEN
是一个包含已签名 API 令牌的环境变量,有关详细信息,请参阅API 令牌文档。
我在这里使用jq
(使用 GPT-3 生成的配方)将抓取的数据转换为 Datasette API 所需的 JSON 格式。结果如下所示:
{ “行” :[ { “编号” : “ 33762438 ” , "title" : "连续项目囤积者的应对策略" , “网址” : “ https://simonwillison.net/2022/Nov/26/productivity/ ” , “dt” : “ 2022-11-27T12:12:56 ” , “点” : 222 , “提交者” : “ usrme ” , “commentsUrl” : “ https://news.ycombinator.com/item?id=33762438 ” , “评论数” : 38 } ], “替换” :真 }
然后将其发布到https://simon.datasette.cloud/data/hacker_news_posts/-/insert
API 端点。
"rows"
键是要插入的行的列表。
"replace": true
告诉 Datasette 用相同的主键替换任何现有的行。否则,如果任何行已存在,API 将返回错误。
API 还接受"ignore": true
这将导致它忽略任何已经存在的行。
完整的插入 API 文档在此处。
最初创建表
在我可以插入任何行之前,我需要创建表格。
我也是从命令行做到的,使用这个食谱:
导出行= $( jq -n --argjson 行“ $( cat simonwillison-net.json ) ” \ ' { "table": "hacker_news_posts", "rows": $rows, "pk": "id" } ' ) #使用 curl 将一些 JSON POST 到 URL 卷曲-X POST \ https://simon.datasette.cloud/data/-/create \ -H “内容类型:应用程序/json ” \ -H "授权:不记名$DS_TOKEN " \ -d $行
这使用了与上面相同的技巧,但点击了不同的 API 端点: /data/-/create
,这是用于在data.db
数据库中创建表的端点。
提交给该端点的 JSON 如下所示:
{ “表” : “ hacker_news_posts ” , “pk” : “ id ” , “行” :[ { “编号” : “ 33762438 ” , "title" : "连续项目囤积者的应对策略" , “网址” : “ https://simonwillison.net/2022/Nov/26/productivity/ ” , “dt” : “ 2022-11-27T12:12:56 ” , “点” : 222 , “提交者” : “ usrme ” , “commentsUrl” : “ https://news.ycombinator.com/item?id=33762438 ” , “评论数” : 38 } ] }
它的形状几乎与上面的/-/insert
调用相同。这是因为它使用了从sqlite-utils继承的 Datasette API 的一个特性——它可以从行列表创建一个表,自动确定正确的模式。
如果您已经知道您的模式,您可以传递一个"columns": [...]
键,但我发现这种自动模式生成在实践中非常有效。
Datasette 将允许您多次调用创建 API,如果表已经存在,它会将新行直接插入现有表中。我希望这是一种非常方便的编写自动化脚本的方法,您不想费心检查表是否已经存在。
构建 Atom 提要
这个演示的最终目标是构建一个我可以在我的 NetNewsWire 提要阅读器中订阅的 Atom 提要。
我已经有一个插件: datasette-atom ,它允许您为 Datasette 中的任何数据生成 Atom 提要,使用 SQL 查询定义。
我为此创建了一个 SQL 视图(使用安装在 Datasette Cloud 上的datasette-write插件):
创建视图hacker_news_posts_atom作为选择 id作为atom_id, 标题为atom_title, 网址, commentsUrl作为atom_link, dt || ' Z '作为atom_updated, '提交者: ' ||提交者 || ' - ' ||积分|| '点, ' ||评论数|| ' comments '作为atom_content 从 黑客新闻帖子 订购方式 数据描述 限制 100 ;
datasette-atom
需要一个返回atom_id
、 atom_title
和atom_updated
列的表、视图或 SQL 查询——如果它们存在,也会使用atom_link
和atom_content
。
Datasette Cloud 默认将所有表和视图保持私有 – 但不久前我创建了datasette-public插件来提供用于公开表的 UI。
事实证明这对 SQL 视图还不起作用,所以我修复了它– 然后使用该选项使我的视图公开。您可以访问它:
https://simon.datasette.cloud/data/hacker_news_posts_atom
要获取 Atom 提要,只需将.atom
添加到 URL 的末尾:
https://simon.datasette.cloud/data/hacker_news_posts_atom.atom
这是它在 NetNewsWire 中的样子:
我对能够以这种方式组合这些工具感到非常兴奋:它使得从抓取的数据到 Datasette 表再到 Atom 提要成为一个非常可重复的过程。
构建一个 TODO 列表应用程序
我的第二个演示探讨了针对新 API 开发自定义应用程序的情况。
TodoMVC是一个项目,它提供使用数十种不同的 JavaScript 框架构建的相同 TODO 列表界面,作为比较工具。
我决定用它来构建我自己的 TODO 列表应用程序,使用 Datasette 作为后端。
您可以在https://todomvc.datasette.io/上试用 – 但请注意,该演示每 15 分钟重置一次,因此不要将其用于真正的任务跟踪!
该演示的源代码位于simonw/todomvc-datasette – 它也使用 GitHub Pages 为演示本身提供服务。
该代码基于 TodoMVC Vanilla JavaScript 示例。我使用了未修改的文件,除了一个文件 – store.js ,我修改它以使用 Datasette API 而不是localStorage
。
该演示当前使用硬编码身份验证令牌,该令牌经过签名以允许以名为todomvc
的用户身份对https://latest.datasette.io/演示实例执行操作。
该用户目前在自定义插件中被授予权限,但我计划在未来提供一种更加用户友好的方式来执行此操作。
几个说明性的代码片段。首先,在页面加载时,此构造函数使用 Datasette API 来创建应用程序使用的表:
函数存储(名称,回调) { 回调=回调||函数( ) { } ; // 确保存在具有此名称的表 让自我=这个; 自我。 _dbName = `todo_ ${ name } ` ; 获取( “https://latest.datasette.io/ephemeral/-/create” , { 方法: “POST” , 模式: “cors” , 标题: { 授权: `Bearer ${ TOKEN } ` , “内容类型” : “应用程序/json” , } , 正文: JSON 。字符串化( { 表:自我。 _数据库名称, 专栏: [ {名称: “id” ,类型: “整数” } , {名称: “标题” ,类型: “文本” } , {名称: “完成” ,类型: “整数” } , ] , pk : “身份证” , } ) , } ) 。然后(函数( r ) { 回调。调用(这个, [ ] ) ; } ) ; }
大多数应用程序会针对已创建的表运行,但这是展示表创建情况的好机会。
请注意,该表是使用/ephemeral/-/create
的——此端点允许您在临时数据库中创建表,该数据库是一个临时数据库,会在 15 分钟后删除每个表。我构建了datasette-ephemeral-tables插件来实现这一点。
下面是创建或更新新的 TODO 列表项时调用的代码:
商店。原型。保存=功能(更新数据,回调, ID ) { // {标题,完成} 回调=回调||函数( ) { } ; 变种表=这个。 _数据库名称; // 如果确实给出了 ID,则查找该项目并更新每个属性 如果(编号) { 获取( `https://latest.datasette.io/ephemeral/ ${ table } / ${ id } /-/update` , { 方法: “POST” , 模式: “cors” , 标题: { 授权: `Bearer ${ TOKEN } ` , “内容类型” : “应用程序/json” , } , 正文: JSON 。字符串化( {更新:更新数据} ) , } ) .然后( ( r ) = > r .json ( ) ) .然后( (数据) => { 回调。呼叫(自我,数据) ; } ) ; }否则{ // 保存并存储 ID 获取( `https://latest.datasette.io/ephemeral/$ {表} /-/插入` , { 方法: “POST” , 模式: “cors” , 标题: { 授权: `Bearer ${ TOKEN } ` , “内容类型” : “应用程序/json” , } , 正文: JSON 。字符串化( { 行:更新数据, } ) , } ) .然后( ( r ) = > r .json ( ) ) .然后( (数据) => { 让行=数据。行[ 0 ] ; 回调。呼叫(自我,行) ; } ) ; } } ;
如果正在更新记录,TodoMVC 会传递一个id
– 此代码将其用作应调用...table/row-id/-/update
API 的标志(请参阅更新 API 文档)。
如果该行没有 ID,则使用table/-/insert
,这次使用"row":
键,因为我们只插入一行。
让它工作最困难的部分是确保 Datasette 的CORS 模式能够正确地进行写入。我必须添加一个新的Access-Control-Allow-Methods
标头,我在Datasette 1.0a1中提供了它(请参阅问题 #1922 )。
试用临时托管 API
我构建了datasette-ephemeral-tables插件,因为我想提供一个写入 API 的演示实例,任何人都可以试用它而无需自己安装 Datasette – 但这不会让我负责处理他们的数据或清理他们的任何混乱。
欢迎您使用https://latest.datasette.io/演示实例来试验 API。
首先,您需要以根用户身份登录。您可以使用此页面上的按钮执行此操作(无需密码)。
登录后,您可以在此处查看临时数据库(匿名用户不可见):
https://latest.datasette.io/ephemeral
您可以使用 API 资源管理器在此处尝试针对它的不同写入 API:
https://latest.datasette.io/-/api
您可以创建自己的签名令牌以访问此页面上的 API:
https://latest.datasette.io/-/create-token
上面描述的 TodoMVC 应用程序也使用ephemeral
数据库,因此如果有人正在玩该演示,您可能会看到todo_todos-vanillajs
表出现在那里。
或者在你自己的机器上运行
您可以像这样安装最新的 Datasette alpha:
pip install datasette==1.0a1
然后创建一个数据库并以root
用户身份登录以获得对 API 的访问权限:
datasette demo.db --create --root
单击它输出的链接以 root 用户身份登录,然后访问 API 资源管理器开始试用 API:
API 浏览器完全不需要令牌,使用您现有的浏览器 cookie。
如果您想使用curl
或类似工具尝试 API,您可以使用此页面为root
用户创建一个新的签名 API 令牌:
如果您重新启动服务器,此令牌将失效,除非您在启动服务器之前将DATASETTE_SECRET
环境变量修复为稳定的字符串:
export DATASETTE_SECRET=$( python3 -c 'print(__import__("secrets").token_hex(16))' )
查看Write API 文档以获取更多详细信息。
下一步是什么?
如果您对这些 API 有反馈,现在是时候分享它了!我希望在 2023 年初发布 Datasette 1.0,之后这些 API 将在未来很长一段时间内被认为是稳定的。
如果您有想法或反馈(或问题),请加入我们的Datasette Discord 。您还可以针对Datasette本身提交问题评论。
对于下一个 1.0 alpha,我的首要任务是对 Datasette 的 JSON API 的其他方面进行少量向后不兼容的更改,我一直希望将这些更改包含在 1.0 中。
我还将向我的Datasette Cloud预览版用户推出 API 支持。如果您有兴趣尝试一下,可以在此处申请访问权限。
原文: http://simonwillison.net/2022/Dec/2/datasette-write-api/#atom-everything