使用 Metabase API

Metabase API 简介。

本文解释了如何使用 Metabase API 自动化任务。我们自己也使用这个 API 来连接前端和后端,因此您可以编写脚本来完成 Metabase 几乎所有能做的事情。

API 参考

您可以在我们的文档中找到 Metabase API 参考 docs。您还可以在自己运行的 Metabase 的 /api/docs 处查看实时 OpenAPI 文档。因此,如果您的 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 'http://localhost: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 http://localhost/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"
}

该请求生成了问题

A question in Metabase generated by the API: a list of the Orders table in the Sample Database

查看 Metabase 的请求和响应

在实时 API 文档中进行实验

您可以从运行中的 Metabase 的 /api/docs 处,通过 RapiDoc 查看实时 OpenAPI 文档。因此,如果您的 Metabase 位于 https://www.your-metabase.com,您可以通过 https://www.your-metabase.com/api/docs 访问它们。

在实时文档中,您可以尝试发送请求并查看示例响应

Example of an API response in live docs

使用开发者工具

如果自动生成的 API 文档不清楚,您可以使用 Firefox、Chrome 和 Edge 等浏览器附带的开发者工具来查看 Metabase 的请求和响应。

Using Firefox

在 Metabase 应用程序中,执行您想要脚本化的操作,例如添加用户或创建仪表盘。然后使用浏览器中的开发者工具查看 Metabase 在您执行该操作时向服务器发出的请求。

您可以使用 Metabase API 完成的几件事

配置 Metabase 实例

除了使用环境变量,您还可以使用 Metabase API 来设置 Metabase 实例。一旦您使用首选方法安装了 Metabase,并且 Metabase 服务器已启动并运行,您可以通过向一个特殊的端点 /api/setup 发送 POST 请求来创建第一个用户(作为管理员)。这个 /api/setup 端点

  • 创建第一个用户作为管理员(超级用户)。
  • 登录他们。
  • 返回一个会话 ID。

然后,您可以使用 /api/settings 端点配置设置,使用 /api/email 端点设置电子邮件,并使用 /api/setup/admin_checklist 端点验证您的设置进度。

Admin checklist for setting up Metabase to make the most of your application.

添加数据源

您可以使用 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' \
    http://localhost: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 "http://localhost:3000/api/user" \
    -H 'Content-Type: application/json' \
    -H 'x-api-key: YOUR_API_KEY' \
    -d '{
    "first_name":"Basic",
    "last_name":"Person",
    "email":"basic@somewhere.com",
    "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 的税前平均值。它是公开共享的——本教程解释了如何做到这一点。

The notebook of a public question calculating the average value of orders over $100 by product category.

第二个问题计算数据库中的人数。它*没有*共享:我们将其包含在内是为了展示如何区分共享和未共享的问题。

The notebook of a non-public question calculating the number of people in the database.

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('http://localhost: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'http://localhost: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'http://localhost: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

R 和 Tidyverse

我们示例的 R 版本与 Python 版本结构相同。像大多数数据科学家一样,我们使用 tidyverse 家族库,所以让我们加载它们以及用于管理 HTTP 请求的 httr、用于解析 JSON 的 jsonlite 和用于字符串格式化的 glue

library(tidyverse)
library(httr)
library(jsonlite)
library(glue)

我们将 API 密钥放在头部。

headers <- add_headers('x-api-key' = YOUR_API_KEY)

然后我们获取所有问题的信息,并询问哪些是公开的。

data <- GET('http://localhost: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('http://localhost: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('http://localhost: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.

JavaScript on Node.js

JavaScript 是一种越来越流行的服务器端脚本语言,但与 Python 和 R 不同,JavaScript 缺乏用于数据表的单一主要库。对于大型项目,我们偏爱 data-forge,但对于小型示例,我们坚持使用 Dataframe-js。我们还使用 got 进行 HTTP 请求,而不是更旧的 request 包,因为后者现已弃用。最后,由于我们发现 async/await 语法比 promise 或回调更容易阅读,我们将所有代码放在一个 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("http://localhost: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(`http://localhost: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(
  `http://localhost: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": "person@metabase.com", "password": "fakepassword"}' \
  http://localhost: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,尽情享受吧!如果您遇到问题,请查看我们的论坛,看看是否有人遇到类似问题,或者发布新问题。

© . All rights reserved.