使用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没有版本号。因此,不要期望为了使用“稳定”的API而停留在Metabase的特定版本。

有关API更改,请查看开发者指南中的API变更日志

开始使用Metabase API

为了使事情简单,我们将使用久经考验的命令行工具curl来展示API调用示例;您也可以考虑使用专门的工具来开发API请求(例如Postman)。为了跟上来,您可以在本地主机上启动一个新的Metabase并尝试一下。

创建API密钥

要使用API,请创建一个API密钥

示例GET请求

以下是一个示例API请求,它调用/api/permissions/group端点,该端点返回您在Metabase中设置的权限组。用您的API密钥替换YOUR_API_KEY

curl \
-H 'x-api-key: YOUR_API_KEY' \
-X GET 'https://127.0.0.1:3000/api/permissions/group'

上述请求返回一个JSON对象数组,代表您Metabase中的组(格式化以提高可读性)

[
  {
    "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"
}

该请求生成了问题

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

查看Metabase的请求和响应

在实时API文档中实验

您可以在运行的Metabase中查看通过RapiDoc提供的实时OpenAPI文档,地址为/api/docs。因此,如果您的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发送请求来创建第一个用户(作为管理员)。

  • /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' \
    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身份验证登录,发送密码重置电子邮件,等等。 /api/sessions
设置 创建/更新全局应用程序设置。 /api/setting
查询 使用API执行查询,并以指定的格式返回其结果。 /api/dataset
问题 问题(在API中称为卡片)是查询及其可视化结果。 /api/card

还有一些其他有趣的端点可以查看,如api/database/:virtual-db/metadata,它用于“欺骗”前端,使其将保存的问题视为虚拟数据库中的表。这就是Metabase允许您将保存的问题作为数据源使用的方式。

文档包含API端点的完整列表以及每个端点的文档,因此请深入挖掘,看看您能找到哪些其他有趣的端点。

端点参考定期更新为Metabase的新版本。您还可以通过运行来生成参考

java -jar metabase.jar api-documentation

运行自定义查询

使用查询构建器编写的查询保存在我们定制的基于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('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

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('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语法比承诺或回调更容易阅读,因此我们将所有代码放入一个立即调用的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替换为您的服务器地址。此请求将返回一个包含名为id的键以及令牌作为该键值的JSON对象,例如:

{ "id": "38f4939c-ad7f-4cbe-ae54-30946daf8593" }

然后您可以将该会话令牌包含在后续请求的头部中,如下所示:

"X-Metabase-Session": "38f4939c-ad7f-4cbe-ae54-30946daf8593"

关于会话的一些注意事项

  • 默认情况下,会话有效期为14天。您可以通过设置环境变量MB_SESSION_AGE(值以分钟为单位)来配置此会话持续时间。
  • 您应该缓存凭据以在它们过期之前重用它们,因为为了安全起见,登录会被速率限制。
  • 无效和过期的会话令牌将返回状态码401(未经授权)。
  • 优雅地处理401状态码。我们建议编写代码以获取新的会话令牌,并在API返回401状态码时自动重试请求。
  • 某些端点要求用户必须是管理员,也称为超级用户。 需要管理员或超级用户权限的端点(admin = 超级用户)通常会在其文档中说明。如果当前用户不是管理员,它们将返回403(禁止访问)状态码

简而言之:使用API密钥代替。

玩得开心

如果你觉得这个教程很有趣,你可以启动一个本地的Metabase实例,实验API,玩得开心!如果你卡住了,可以查看我们的论坛,看看是否有人遇到过类似的问题,或者提出一个新的问题。