datasette-graphql 作者 simonw

星标

Screenshot of simonw/datasette-graphql

README 源代码

datasette-graphql

PyPI Changelog Tests License

Datasette 插件,为您的 SQLite 数据库提供自动的 GraphQL API

阅读更多关于此项目的信息: 使用新的 datasette-graphql 插件在 Datasette 中使用 GraphQL

在线试用演示,请访问 datasette-graphql-demo.datasette.io/graphql

Animated demo showing autocomplete while typing a GraphQL query into the GraphiQL interface

安装

在与 Datasette 相同的环境中安装此插件。

$ datasette install datasette-graphql

配置

默认情况下,此插件在 /graphql 添加 GraphQL API。您可以使用 path 插件设置配置不同的路径,例如将此添加到 metadata.json

{
  "plugins": {
    "datasette-graphql": {
      "path": "/-/graphql"
    }
  }
}

这将把 GraphQL API 设置为位于 /-/graphql

用法

此插件将 /graphql 设置为第一个附加数据库的 GraphQL 端点。

如果您有多个附加数据库,每个数据库都将在 /graphql/name_of_database 拥有自己的端点。

自动生成的 GraphQL schema 可在 /graphql/name_of_database.graphql 获取 - 这里是一个示例

查询表和列

可以像这样查询单个表(和 SQL 视图)

{
  repos {
    nodes {
      id
      full_name
      description_
    }
  }
}

尝试此查询

在此示例查询中,底层数据库表名为 repos,其列包括 idfull_namedescription。由于 description 是保留字,查询需要使用 description_

获取单条记录

如果您只想获取单条记录 - 例如,如果您想通过主键获取行 - 您可以使用 tablename_row 字段

{
  repos_row(id: 107914493) {
    id
    full_name
    description_
  }
}

尝试此查询

tablename_row 字段接受主键列(或多个列)作为参数。它还支持与 tablename 字段相同的 filter:search:sort:sort_desc: 参数,如下所述。

访问嵌套对象

如果某个列是另一个表的外键,您可以使用嵌套查询来请求该外键指向的表中的列,如下所示

{
  repos {
    nodes {
      id
      full_name
      owner {
        id
        login
      }
    }
  }
}

尝试此查询

访问相关对象

如果另一个表有一个外键指向您正在访问的表,您可以从该相关表中获取行。

考虑一个与 repos 相关的 users 表 - 一个 repo 有一个外键指向拥有该 repository 的用户。users 对象类型将有一个 repos_by_owner_list 字段,可用于访问这些相关的 repos

{
  users(first: 1, search: "simonw") {
    nodes {
      name
      repos_by_owner_list(first: 5) {
        totalCount
        nodes {
          full_name
        }
      }
    }
  }
}

尝试此查询

过滤表

您可以使用 filter: 参数过滤特定表返回的行。它接受一个 filter 对象,将列映射到操作。例如,要仅返回使用 Apache 2 许可且星标数超过 10 的 repositories

{
  repos(filter: {license: {eq: "apache-2.0"}, stargazers_count: {gt: 10}}) {
    nodes {
      full_name
      stargazers_count
      license {
        key
      }
    }
  }
}

尝试此查询

有关更多操作,请参阅表过滤器示例,有关这些操作如何工作的详细信息,请参阅 Datasette 文档中的列过滤器参数

这些相同的过滤器也可用于嵌套关系,如下所示

{
  users_row(id: 9599) {
    name
    repos_by_owner_list(filter: {name: {startswith: "datasette-"}}) {
      totalCount
      nodes {
        full_name
      }
    }
  }
}

尝试此查询

当您要表达的内容过于复杂而无法使用 filter 表达式建模时,where: 参数可以作为 filter: 的替代。它接受一段 SQL 字符串片段,该片段将包含在 SQL 查询的 WHERE 子句中。

{
  repos(where: "name='sqlite-utils' or name like 'datasette-%'") {
    totalCount
    nodes {
      full_name
    }
  }
}

尝试此查询

排序

您可以使用 sort:sort_desc: 参数设置表结果的排序顺序。此参数的值应为您希望排序(或降序排序)的列的名称。

{
  repos(sort_desc: stargazers_count) {
    nodes {
      full_name
      stargazers_count
    }
  }
}

尝试此查询

分页

默认情况下,将返回前 10 行。您可以使用 first: 参数控制此行为。

{
  repos(first: 20) {
    totalCount
    pageInfo {
      hasNextPage
      endCursor
    }
    nodes {
      full_name
      stargazers_count
      license {
        key
      }
    }
  }
}

尝试此查询

totalCount 字段返回与查询匹配的记录总数。

请求 pageInfo.endCursor 字段会为您提供请求下一页所需的值。您可以将其传递给 after: 参数来请求下一页。

{
  repos(first: 20, after: "134874019") {
    totalCount
    pageInfo {
      hasNextPage
      endCursor
    }
    nodes {
      full_name
      stargazers_count
      license {
        key
      }
    }
  }
}

尝试此查询

hasNextPage 字段告诉您是否还有更多记录。

搜索

如果表已配置为使用 SQLite 全文搜索,您可以使用 search: 参数对其执行搜索

{
  repos(search: "datasette") {
    totalCount
    pageInfo {
      hasNextPage
      endCursor
    }
    nodes {
      full_name
      description_
    }
  }
}

尝试此查询

可以使用 sqlite-utils Python 库和 CLI 工具向现有数据库表添加全文搜索。

包含 JSON 字符串的列

如果您的表包含一个包含 JSON 编码数据的列,datasette-graphql 将使该列作为编码的 JSON 字符串可用。调用您的 API 的客户端需要将该字符串解析为 JSON 才能访问数据。

您可以通过配置该列作为 JSON 列处理,将数据作为嵌套结构返回。在 metadata.json 中的插件配置如下所示

{
    "databases": {
        "test": {
            "tables": {
                "repos": {
                    "plugins": {
                        "datasette-graphql": {
                            "json_columns": [
                                "tags"
                            ]
                        }
                    }
                }
            }
        }
    }
}

自动 camelCase

您的列和表的名称默认与其在 GraphQL 中的表示匹配。

如果您有使用 names_like_this 命名方式的表,为了与 GraphQL 和 JavaScript 约定保持一致,您可能希望在 GraphQL 中使用 namesLikeThis

您可以在 metadata.json 中使用 "auto_camelcase" 插件配置设置开启自动 camelCase,如下所示

{
    "plugins": {
        "datasette-graphql": {
            "auto_camelcase": true
        }
    }
}

CORS

此插件遵循传递给 datasette 命令行工具的 --cors 选项。如果您传递 --cors,它将添加以下 CORS HTTP 头部,以允许在其他域上运行的 JavaScript 访问 GraphQL API

access-control-allow-headers: content-type
access-control-allow-method: POST
access-control-allow-origin: *

执行限制

默认情况下,该插件实现了两个限制

  • 执行组成 GraphQL 执行的所有底层 SQL 查询所花费的总时间不得超过 1000 毫秒(一秒)
  • 由于嵌套的 GraphQL 字段而执行的 SQL 表查询总数不得超过 100

可以使用 num_queries_limittime_limit_ms 插件配置设置来自定义这些限制,例如在 metadata.json

{
    "plugins": {
        "datasette-graphql": {
            "num_queries_limit": 200,
            "time_limit_ms": 5000
        }
    }
}

将这些值设置为 0 将完全禁用限制检查。

graphql() 模板函数

该插件还提供了一个名为 graphql() 的 Jinja 模板函数。您可以在 Datasette 的自定义模板中使用该函数,如下所示

{{ user.name }} - 积分: {{ user.points }}, 分数 = {{ user.score }}

{% endfor %}">
{% set users = graphql("""
{
    users {
        nodes {
            name
            points
            score
        }
    }
}
""")["users"] %}
{% for user in users.nodes %}
    <p>{{ user.name }} - points: {{ user.points }}, score = {{ user.score }}p>
{% endfor %}

该函数针对生成的 schema 执行 GraphQL 查询并返回结果。您可以将这些结果分配给模板中的变量,然后遍历并显示它们。

默认情况下,查询将针对第一个附加数据库运行。您可以使用函数的第二个可选参数来指定不同的数据库 - 例如,要针对附加的 github.db 数据库运行,您可以这样做

你好, {{ user.name }}">
{% set user = graphql("""
{
    users_row(id:9599) {
        name
        login
        avatar_url
    }
}
""", "github")["users_row"] %}

<h1>Hello, {{ user.name }}h1>

您可以通过将GraphQL 变量传递给 variables= 参数,在这些模板调用中使用它们

你好, {{ user.name }}">
{% set user = graphql("""
query ($id: Int) {
    users_row(id: $id) {
        name
        login
        avatar_url
    }
}
""", database="github", variables={"id": 9599})["users_row"] %}

<h1>Hello, {{ user.name }}h1>

使用插件添加自定义字段

datasette-graphql 向 Datasette 添加了一个新的插件钩子,可用于向您的 GraphQL schema 添加自定义字段。

插件钩子如下所示

@hookimpl
def graphql_extra_fields(datasette, database):
    "A list of (name, field_type) tuples to include in the GraphQL schema"

您可以使用此钩子返回一个元组列表,描述应在您的 schema 中公开的额外字段。每个元组应包含一个命名新字段的字符串,以及一个指定 schema 并提供 resolver 函数的Graphene Field 对象

此示例实现使用 pkg_resources 返回当前已安装的 Python 包列表

import graphene
from datasette import hookimpl
import pkg_resources


@hookimpl
def graphql_extra_fields():
    class Package(graphene.ObjectType):
        "An installed package"
        name = graphene.String()
        version = graphene.String()

    def resolve_packages(root, info):
        return [
            {"name": d.project_name, "version": d.version}
            for d in pkg_resources.working_set
        ]

    return [
        (
            "packages",
            graphene.Field(
                graphene.List(Package),
                description="List of installed packages",
                resolver=resolve_packages,
            ),
        ),
    ]

安装此插件后,可以使用以下 GraphQL 查询来检索已安装的包列表

{
  packages {
    name
    version
  }
}

开发

要在本地设置此插件,首先 checkout 代码。然后创建一个新的虚拟环境

cd datasette-graphql
python3 -mvenv venv
source venv/bin/activate

或者如果您正在使用 pipenv

pipenv shell

现在安装依赖项和测试

pip install -e '.[test]'

运行测试

pytest