使用 Metabase API
Metabase API 简介。
本文介绍了如何使用 Metabase 的 API 自动化任务。我们自己使用该 API 连接前端和后端,因此您可以编写脚本来完成 Metabase 可以做的几乎所有事情。
API 参考
您可以在我们的文档中找到 Metabase API 参考。您还可以在您自己运行的 Metabase 中查看实时的 OpenAPI 文档,网址为 /api/docs
。因此,如果您的 Metabase 位于 https://www.your-metabase.com
,您可以通过 https://www.your-metabase.com/api/docs
访问它们。
警告:Metabase API 可能会更改
- API 可能会更改。我们很少更改 API 端点,几乎从不删除它们,但是如果您编写的代码依赖于 API,则您将来可能必须更新您的代码。
- API 没有版本控制。因此,不要期望停留在 Metabase 的特定版本上以使用“稳定”的 API。
有关 API 更改,请查看开发者指南的 API 变更日志。
Metabase API 入门
为了保持简单,我们将使用历史悠久的命令行实用程序 curl 作为我们的 API 调用示例;您也可以考虑使用专门的工具来开发 API 请求(例如 Postman)。要继续学习,您可以 在 localhost 上启动一个新的 Metabase 并进行尝试。
创建 API 密钥
要使用 API,请创建 API 密钥。
示例 GET 请求
这是一个 API 请求示例,它访问 /api/permissions/group
端点,该端点返回您在 Metabase 中设置的权限组。将 YOUR_API_KEY
替换为您的 API 密钥
curl \
-H 'x-api-key: YOUR_API_KEY' \
-X GET 'https://127.0.0.1:3000/api/permissions/group'
上面的请求返回 Metabase 中组的 JSON 对象数组(为提高可读性而格式化)
[
{
"id": 2,
"name": "Administrators",
"member_count": 2
},
{
"id": 1,
"name": "All Users",
"member_count": 3
}
]
示例 POST 请求
您还可以使用文件来存储 POST 请求的 JSON 负载。这使得拥有您想要向 API 发出的一组预定义请求变得容易。
curl -H @header_file.txt -d @payload.json https://127.0.0.1/api/card
这是上面命令中的 header_file.text
x-api-key: YOUR_API_KEY
这是一个 JSON 文件示例(上面命令中的 @payload.json
),用于创建问题
{
"visualization_settings": {
"table.pivot_column": "QUANTITY",
"table.cell_column": "SUBTOTAL"
},
"description value": "A card generated by the API",
"collection_position": null,
"result_metadata": null,
"metadata_checksum": null,
"collection_id": null,
"name": "API-generated question",
"dataset_query": {
"database": 1,
"query": {
"source-table": 2
},
"type": "query"
},
"display": "table"
}
该请求生成了问题
查看 Metabase 的请求和响应
在实时 API 文档中进行实验
您可以从您运行的 Metabase 中的 /api/docs
查看通过 RapiDoc 提供的实时 OpenAPI 文档。因此,如果您的 Metabase 位于 https://www.your-metabase.com
,您可以通过 https://www.your-metabase.com/api/docs
访问它们。
在实时文档中,您可以尝试发送请求并查看示例响应
使用开发者工具
如果自动生成的 API 文档不清楚,您可以使用 Firefox、Chrome 和 Edge 等浏览器附带的开发者工具来查看 Metabase 的请求和响应。
在 Metabase 应用程序中,执行您想要编写脚本的操作,例如添加用户或创建仪表板。然后使用浏览器中的开发者工具查看当您执行该操作时 Metabase 向服务器发出的请求。
您可以使用 Metabase API 执行的一些操作
配置 Metabase 实例
除了使用 环境变量 之外,您还可以使用 Metabase API 来设置 Metabase 实例。使用您首选的方法安装 Metabase 后,并且 Metabase 服务器已启动并运行,您可以通过发布到特殊端点 /api/setup 来创建第一个用户(作为管理员)。此 /api/setup
端点
- 将第一个用户创建为管理员(超级用户)。
- 使其登录。
- 返回会话 ID。
然后,您可以使用 /api/settings
端点配置设置,使用 /api/email
端点设置电子邮件,并使用 /api/setup/admin_checklist
端点验证您的设置进度。
添加数据源
您可以使用 POST /api/database/
端点添加新数据库,并使用 /api/setup/validate
端点验证该数据库的连接详细信息。将数据库连接到您的 Metabase 实例后,您可以重新扫描数据库并更新架构元数据。您甚至可以使用 POST /api/database/sample_database
将我们可靠的 示例数据库 作为新数据库添加到您的实例中。
这是一个 Redshift 数据库的数据库创建调用示例。
curl -s -X POST \
-H "Content-type: application/json" \
-H 'x-api-key: YOUR_API_KEY' \
https://127.0.0.1:3000/api/database \
-d '{
"engine": "redshift",
"name": "Redshift",
"details": {
"host": "redshift.aws.com",
"port": "5432",
"db": "dev",
"user": "root",
"password": "password"
}
}'
设置用户、组和权限
您可以使用 /api/user
端点来创建、更新和禁用用户,或使用 /api/permissions
端点来设置组或将用户添加到组。这是一个创建用户的 curl 命令示例
curl -s "https://127.0.0.1:3000/api/user" \
-H 'Content-Type: application/json' \
-H 'x-api-key: YOUR_API_KEY' \
-d '{
"first_name":"Basic",
"last_name":"Person",
"email":"[email protected]",
"password":"Sup3rS3cure_:}"
}'
生成报告
在 Metabase 中,“报告”称为 仪表板。您可以使用 /api/dashboard
端点与仪表板进行交互。您可以使用 POST /api/dashboard/
创建新仪表板,并使用 [POST/api/dashboard/:id/cards
] 将已保存的问题添加到仪表板。
有用的端点
下面“端点”列中的链接会将您带到该端点的第一个可用操作,该操作在字母顺序上通常是 DELETE 操作。您可以在 API 文档中向下滚动以查看该端点的完整操作和 URL 列表,并查看每个操作的描述。
领域 | 描述 | 端点 |
---|---|---|
集合 | 集合是组织仪表板、已保存的问题和脉冲的好方法。 | /api/collection |
仪表板 | 仪表板是包含一组问题和文本卡的报告。 | /api/dashboard |
数据库 | 获取数据库、字段、架构、主(实体)键、表列表等。 | /api/database |
电子邮件 | 更新电子邮件设置并发送测试电子邮件。 | /api/email |
嵌入 | 使用签名的 JWT 获取有关嵌入式卡片和仪表板的信息。 | /api/embed |
权限 | Metabase 使用组管理对数据库和集合的权限。创建权限组,向组添加和删除用户,检索所有权限组的图表等等。 | /api/permissions |
搜索 | 搜索卡片(问题)、仪表板、集合和脉冲的子字符串。 | /api/search |
分段 | 分段是命名的过滤器集(例如“活跃用户”)。创建和更新分段,恢复到以前的版本等等。 | /api/segment |
会话 | 使用令牌重置密码,使用 Google Auth 登录,发送密码重置电子邮件等等。 | /api/sessions |
设置 | 创建/更新全局应用程序设置。 | /api/setting |
查询 | 使用 API 执行查询并以指定格式返回其结果。 | /api/dataset |
问题 | 问题(在 API 中称为卡片)是查询及其可视化结果。 | /api/card |
还有一些其他很酷的端点可以查看,例如 api/database/:virtual-db/metadata
,它用于“欺骗”前端,以便它可以将已保存的问题视为虚拟数据库中的表。这就是 Metabase 允许您像数据源一样使用已保存问题的方式。
文档包含 API 端点的完整列表以及每个端点的文档,因此请深入研究并查看您可以找到的其他很酷的端点。
端点参考会定期使用新版本的 Metabase 进行更新。您还可以通过运行以下命令生成参考
java -jar metabase.jar api
运行自定义查询
使用查询构建器编写的查询保存在我们自定义的基于 JSON 的查询语言 MBQL 中。
为了让您熟悉 MBQL,我们建议您使用 Metabase 应用程序使用查询构建器创建一个问题),然后使用浏览器的开发者工具查看 Metabase 如何使用查询格式化请求正文。
Python、R 和 JavaScript 中的示例
Curl 是探索 API 的便捷工具,但如果您要将 Metabase 集成到大型数据生态系统中,您可能会使用其他工具。为了展示如何使用 Python、R 和 Node.js 访问 API,让我们创建两个问题。第一个问题查找类别分组的 100 美元以上订单的平均税前价值。它是公开共享的——本教程解释了如何做到这一点。
第二个问题计算数据库中的人数。它未共享:我们包含它是为了展示如何区分共享问题和未共享问题。
Python
我们的第一个示例是用 Python 编写的。像大多数数据科学程序一样,它使用 requests 库发送 HTTP 请求,并使用 Pandas 管理表格数据,因此我们首先导入这些库。
让我们询问 Metabase 哪些问题具有公共 ID,即哪些问题已被共享以便我们可以远程调用它们。当我们请求所有卡片时,我们会得到一个列表,其中包含有关所有问题的一些信息;只有具有 public_uuid
字段的卡片才是可调用的
import requests
import pandas as pd
# Avoid committing your API KEY to the repository
headers = {'x-api-key': YOUR_API_KEY}
response = requests.get('https://127.0.0.1:3000/api/card',
headers=headers).json()
questions = [q for q in response if q['public_uuid']]
print(f'{len(questions)} public of {len(response)} questions')
在我们的示例中,输出告诉我们有两个问题,但只有一个是公共的
1 public of 2 questions
让我们获取有关该公共问题的一些信息并打印其标题
uuid = questions[0]['public_uuid']
response = requests.get(f'https://127.0.0.1:3000/api/public/card/{uuid}',
headers=headers)
print(f'First title: {response.json()["name"]}')
First title: Average value of orders over $100 grouped by category
最后,我们可以从列表中的第一个问题中提取数据。JSON 响应中的 'data'
键包含大量信息;我们最感兴趣的是子键 'rows'
下的值,它以通常的列表形式存储结果表。让我们将其转换为 Pandas 数据帧并打印出来
response = requests.get(f'https://127.0.0.1:3000/api/public/card/{uuid}/query',
headers=headers)
rows = response.json()['data']['rows']
data = pd.DataFrame(rows, columns=['Category', 'Average'])
print('First data')
print(data)
First data
Category Average
0 Doohickey 114.679742
1 Gadget 123.530916
2 Gizmo 120.897286
3 Widget 122.078721
带有 Tidyverse 的 R
我们的示例的 R 版本与 Python 版本具有相同的结构。像大多数数据科学家一样,我们使用 tidyverse 系列库,因此让我们加载这些库以及 httr
用于管理 HTTP 请求,jsonlite
用于解析 JSON,以及 glue
用于字符串格式化
library(tidyverse)
library(httr)
library(jsonlite)
library(glue)
我们将 API 密钥放在标头中。
headers <- add_headers('x-api-key' = YOUR_API_KEY)
然后,我们获取有关所有问题的信息,并询问哪些是公共的
data <- GET('https://127.0.0.1:3000/api/card', headers) %>%
content(as = 'text', encoding = 'UTF-8') %>%
fromJSON()
num_questions <- data %>%
nrow()
num_public <- data %>%
pull(public_uuid) %>%
discard(is.na) %>%
length()
glue('{num_public} public of {num_questions} questions')
1 public of 2 questions
显示第一个公共卡片的标题给出的结果与 Python 相同,这令人放心
uuid <- data %>%
pull(public_uuid) %>%
discard(is.na) %>%
first()
data <- glue('https://127.0.0.1:3000/api/public/card/{uuid}') %>%
GET(headers) %>%
content(as = 'text', encoding = 'UTF-8') %>%
fromJSON()
glue('First title: {data$name}')
First title: Average value of orders over $100 grouped by category
一旦我们将其转换为 tibble,与该卡片关联的数据也相同,尽管 R 的默认显示没有给我们那么多小数位
data <- glue('https://127.0.0.1:3000/api/public/card/{uuid}/query') %>%
GET(headers) %>%
content(as = 'text', encoding = 'UTF-8') %>%
fromJSON()
rows <- data$data$rows
colnames(rows) <- c('Category', 'Average')
rows <- rows %>% as_tibble()
rows$Average <- as.numeric(rows$Average)
glue('First data')
rows
First data
# A tibble: 4 x 2
Category Average
<chr> <dbl>
1 Doohickey 115.
2 Gadget 124.
3 Gizmo 121.
4 Widget 122.
Node.js 上的 JavaScript
JavaScript 是一种越来越流行的服务器端脚本语言,但与 Python 和 R 不同,JavaScript 缺少用于数据表的单个主要库。对于大型项目,我们喜欢 data-forge
,但对于小型示例,我们坚持使用 Dataframe-js。我们还使用 got
进行 HTTP 请求,而不是较旧的 request
包,因为后者现在已被弃用。最后,由于我们发现 async
/await
语法比 promises 或回调更容易阅读,因此我们将所有代码放在一个 async
函数中,然后立即调用该函数
const got = require("got");
const DataFrame = require("dataframe-js").DataFrame;
const main = async () => {
// ...program goes here...
};
main();
再次,我们首先验证自己的身份
headers = { "x-api-key": YOUR_API_KEY };
然后,我们请求问题的完整列表并筛选它们以选择公共问题
response = await got.get("https://127.0.0.1:3000/api/card", {
responseType: "json",
headers: headers,
});
// filter for public questions
questions = response.body.filter((q) => q.public_uuid);
console.log(`${questions.length} public of ${response.body.length} questions`);
1 public of 2 questions
第一个公共卡片仍然具有我们之前看到的标题
const uuid = questions[0].public_uuid;
response = await got.get(`https://127.0.0.1:3000/api/public/card/${uuid}`, {
responseType: "json",
headers: headers,
});
console.log(`First title: ${response.body.name}`);
First title: Average value of orders over $100 grouped by category
当我们提取其数据时,我们得到相同的值,尽管数字以另一种略有不同的方式显示
response = await got.get(
`https://127.0.0.1:3000/api/public/card/${uuid}/query`,
{
responseType: "json",
headers: headers,
},
);
const rows = response.body.data.rows;
const df = new DataFrame(rows, ["Category", "Average"]);
df.show();
| Category | Average |
------------------------
| Doohickey | 114.67... |
| Gadget | 123.53... |
| Gizmo | 120.89... |
| Widget | 122.07... |
使用会话令牌验证您的请求
您应该改为使用 API 密钥。包括以下信息只是以防万一您出于任何原因需要使用会话令牌。
您还可以使用会话令牌来验证您的请求。要获取会话令牌,请使用您的用户名和密码向 /api/session
端点提交请求
curl -X POST \
-H "Content-Type: application/json" \
-d '{"username": "[email protected]", "password": "fakepassword"}' \
https://127.0.0.1:3000/api/session
如果您正在使用远程服务器,则需要将 localhost:3000
替换为您的服务器地址。此请求将返回一个 JSON 对象,其中包含一个名为 id
的键,令牌作为该键的值,例如
{ "id": "38f4939c-ad7f-4cbe-ae54-30946daf8593" }
然后,您可以像这样在后续请求的标头中包含该会话令牌
"X-Metabase-Session": "38f4939c-ad7f-4cbe-ae54-30946daf8593"
关于会话的一些注意事项
- 默认情况下,会话有效期为 14 天。您可以通过设置环境变量
MB_SESSION_AGE
(值以分钟为单位)来配置此会话持续时间。 - 您应该缓存凭据以重复使用它们,直到它们过期,因为出于安全原因,登录会受到速率限制。
- 无效和过期的会话令牌返回 401(未授权)状态代码。
- 优雅地处理 401 状态代码。 我们建议您编写代码以获取新的会话令牌,并在 API 返回 401 状态代码时自动重试请求。
- 某些端点要求用户是管理员,也称为超级用户。 需要管理员或超级用户状态(管理员 = 超级用户)的端点通常会在其文档中说明。 如果当前用户不是管理员,它们将返回 403(禁止访问)状态代码。
简而言之:请改用 API 密钥。
玩得开心
如果您觉得本教程有趣,可以启动 Metabase 的本地实例,体验 API,玩得开心! 如果您遇到困难,请查看我们的论坛,看看是否有人遇到类似的问题,或发布新问题。