跳至正文
Wener Notes

Hasura

约 6 分钟阅读
    • Apache-2.0, MIT, Haskell
  • Working with schemaless data using GraphQL on Postgres
  • 配置
    • --unauthorized-role/HASURA_GRAPHQL_UNAUTHORIZED_ROLE - 匿名角色
      • 例如配置为 anonymous
    • --admin-secret/HASURA_GRAPHQL_ADMIN_SECRET - 管理员密钥
    • --cors-domain <DOMAINS>/HASURA_GRAPHQL_CORS_DOMAIN - CORS 域名
    • --disable-cors
    • --enable-telemetry <true|false>/HASURA_GRAPHQL_ENABLE_TELEMETRY
  • 接口文档
    • /v1/graphql - 主要的 GraphQL 入口 - 生产可以只暴露这一个
  • Hasura 的系统信息存储在 hdb_catalog 和 hdb_view 中
  • HGE - Hasura GraphQL Engine
bash
# 创建一个数据库
USERNAME=hasura
PASSWORD=hasura
DATABASE=hasura
# 登陆 console 使用的 token
TOKEN=$(cat /dev/urandom | env LC_CTYPE=C tr -dc 'a-zA-Z0-9' | head -c 32 | tee -)

docker run -d \
  -e POSTGRES_USER=$USERNAME -e POSTGRES_PASSWORD=$PASSWORD -e POSTGRES_DB=$DATABASE \
  -v $PWD/postgres/data:/var/lib/postgresql/data -p 5432:5432 \
  --name postgres postgres:12-alpine

# 使用已有数据库
# 控制台 http://localhost:8080/console
# https://hub.docker.com/r/hasura/graphql-engine
docker run -it --rm -p 8080:8080 \
  --link postgres:db \
  -e HASURA_GRAPHQL_DATABASE_URL=postgres://$USERNAME:$PASSWORD@db:5432/$DATABASE \
  -e HASURA_GRAPHQL_ENABLE_CONSOLE=true \
  -e HASURA_GRAPHQL_ADMIN_SECRET=$TOKEN \
  -e HASURA_GRAPHQL_UNAUTHORIZED_ROLE=anonymous \
  -e HASURA_GRAPHQL_ENABLE_TELEMETRY=false \
  --name hasura hasura/graphql-engine:latest
sql
-- Hasura 生成 UUID 需要 pgcrypto
CREATE EXTENSION IF NOT EXISTS pgcrypto;

JWT 鉴权

  • 通过 --jwt-secret 启用 - 需要配置 HASURA_GRAPHQL_JWT_SECRET
  • 通过头传递 JWT 信息 Authorization: Bearer <JWT>
  • X-Hasura-Role 当前角色

HASURA_GRAPHQL_JWT_SECRET

json
{
  // 加密算法 - 例如 对称加密 HS* 或 非对称加密 RS*
  "type": "<standard-JWT-algorithms>",
  // 对称加密使用密码 - HMAC
  // 非对称则为公钥 - 可以使用 JWKs 而不配置
  "key": "<optional-key-as-string>",
  // well known 的 url - 包含公钥等信息
  "jwk_url": "<optional-url-to-refresh-jwks>",
  // 命名空间 - 默认 https://hasura.io/jwt/claims
  "claims_namespace": "https://hasura.io/jwt/claims",
  // jwt claims 中的格式 - JSON 对象或字符串编码的 JSON
  "claims_format": "json|stringified_json",
  // jwt 目标对象 - 例如指定 myapp-1 - 一般用于多租户或多应用
  "audience": "<optional-string-or-list-of-strings-to-verify-audience>",
  // 在校验时验证发送方
  "issuer": "<optional-string-to-verify-issuer>"
}

JWT 内容

json
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true,
  "iat": 1516239022,
  "https://hasura.io/jwt/claims": {
    "x-hasura-allowed-roles": ["user", "anonymous"],
    "x-hasura-default-role": "user",
    "x-hasura-user-id": "1234567890",
    "x-hasura-org-id": "123",
    "x-hasura-custom": "custom-value"
  }
}
  • x-hasura-default-role
    • 默认角色 - 可以直接头部指定 x-hasura-role
    • 权限判断会使用该角色
  • x-hasura-allowed-roles
    • 允许的角色 - 自定义 - 对应后台配置的权限
  • hasura.io/jwt/claims
    • claims_namespace
    • claims_namespace_path

授权访问控制

  • X-Hasura-Admin-Secret 头用于传递管理员密钥 - admin 角色
  • Authorization: Bearer <JWT> JWT 鉴权
  • 基于角色的权限控制 - 不支持级联角色 - 角色必须平坦
    • X-Hasura-Role
    • X-Hasura-Allowed-Roles
    • X-Hasura-Allowed-Ids
      • 支持数组
  • 列控制
    • 可见性
  • 行控制
    • 自定义表达式
      • {"user_id":{"_eq":"X-Hasura-User-Id"}} - 其中的 X-Hasura-* 会被替换
  • 操作控制
    • CRUD
  • 针对不同角色会生成不同 schema
  • JWT
    • 必须包含字段 x-hasura-default-role, x-hasura-allowed-roles
    • 头里可指定 x-hasura-role 来选定角色
  • x-hasura-use-backend-only-permissions: true
    • 针对 backend_only 角色
json
{ "user_id": { "_eq": "X-Hasura-User-Id" } }

中间关系

json
{
  "_or": [
    { "owners": { "user_id": { "_eq": "X-Hasura-User-Id" } } },
    { "editors": { "user_id": { "_eq": "X-Hasura-User-Id" } } },
    { "viewers": { "user_id": { "_eq": "X-Hasura-User-Id" } } }
  ]
}

继承

json
{
  "_exists": {
    "_table": {
      "table": "flattened_user_roles",
      "schema": "public"
    },
    "_where": {
      "_and": [{ "user_id": { "_eq": "X-Hasura-User-Id" } }, { "role_id": { "_eq": "manager" } }]
    }
  }
}
  • flattened_user_roles 为视图
sql
CREATE OR REPLACE VIEW flattened_user_roles AS
WITH RECURSIVE sub_tree AS (
  SELECT
    roles.id AS role_id,
    user_roles.user_id AS user_id
  FROM
    user_roles,
    roles
  WHERE
    roles.id = user_roles.role_id
  UNION ALL
  SELECT
    r.id AS role_id,
    st.user_id AS user_id
  FROM
    roles r,
    sub_tree st
  WHERE
    r.parent_role_id = st.role_id
)
SELECT
  *
FROM
  sub_tree;

Server

ini
# 应用相关
HASURA_GRAPHQL_ADMIN_SECRET=$(uuidgen)
HASURA_GRAPHQL_JWT_SECRET='{"type": "HS256", "key": "$(uuidgen | tr -d -)","claims_namespace": "hasura"}'
# 数据库
HASURA_GRAPHQL_DATABASE_URL="postgres://hasura:hasura@postgres:5432/hasura?connect_timeout=10&application_name=hasura"
# 默认 HASURA_GRAPHQL_DATABASE_URL
HASURA_GRAPHQL_METADATA_DATABASE_URL="postgres://hasura:hasura@postgres:5432/hasura"
HASURA_GRAPHQL_CORS_DOMAIN="https://*.wener.me,http://localhost:9695"
# 可以在创建数据源时引用
APP_DATABASE_URL="postgres://hasura:hasura@postgres:5432/hasura?connect_timeout=10&application_name=hasura"

# 公共
HASURA_GRAPHQL_METADATA_DATABASE_EXTENSIONS_SCHEMA=hasura
HASURA_GRAPHQL_DEV_MODE=false
HASURA_GRAPHQL_ENABLE_MAINTENANCE_MODE=false
HASURA_GRAPHQL_ENABLE_METADATA_QUERY_LOGGING=false
HASURA_GRAPHQL_LOG_LEVEL=info
HASURA_GRAPHQL_ENABLE_REMOTE_SCHEMA_PERMISSIONS=true
HASURA_GRAPHQL_EXPERIMENTAL_FEATURES=naming_convention
HASURA_GRAPHQL_DEFAULT_NAMING_CONVENTION=graphql-default
HASURA_GRAPHQL_ENABLE_CONSOLE=true
HASURA_GRAPHQL_UNAUTHORIZED_ROLE=anonymous
HASURA_GRAPHQL_ENABLE_TELEMETRY=false

# 其他
HASURA_GRAPHQL_AUTH_HOOK=
HASURA_GRAPHQL_AUTH_HOOK_MODE=GET # POST
HASURA_GRAPHQL_AUTH_HOOK_SEND_REQUEST_BODY=true

HASURA_GRAPHQL_DISABLE_CORS=false
HASURA_GRAPHQL_ENABLE_ALLOWLIST=false # 预先配置的 Query/Mutation
HASURA_GRAPHQL_ENABLE_MAINTENANCE_MODE=false # 维护模式 - 不会更新 metadata
HASURA_GRAPHQL_ENABLE_METADATA_QUERY_LOGGING=false # 记录 metadata 查询


# 还支持 metrics
HASURA_GRAPHQL_ENABLED_APIS=metadata,graphql,pgdump,config
HASURA_GRAPHQL_ENABLED_LOG_TYPES=startup,http-log,webhook-log,websocket-log
HASURA_GRAPHQL_EVENTS_HTTP_POOL_SIZE=10
HASURA_GRAPHQL_EVENTS_FETCH_BATCH_SIZE=100
HASURA_GRAPHQL_EVENTS_FETCH_INTERVAL=

# inherited_roles, optimise_permission_filters, naming_convention, streaming_subscriptions, apollo_federation, hide_update_many_fields, bigquery_string_numeric_input, hide_aggregation_predicates, hide_stream_fields
HASURA_GRAPHQL_EXPERIMENTAL_FEATURES=
HASURA_GRAPHQL_GRACEFUL_SHUTDOWN_TIMEOUT=60
HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_REFETCH_INTERVAL=1000
HASURA_GRAPHQL_LIVE_QUERIES_MULTIPLEXED_BATCH_SIZE=100

HASURA_GRAPHQL_INFER_FUNCTION_PERMISSIONS=true
HASURA_GRAPHQL_ENABLE_APOLLO_FEDERATION=false

HASURA_GRAPHQL_SCHEMA_SYNC_POLL_INTERVAL=1000
HASURA_GRAPHQL_SERVER_HOST=*
HASURA_GRAPHQL_SERVER_PORT=8080
HASURA_GRAPHQL_STREAMING_QUERIES_MULTIPLEXED_BATCH_SIZE=100
HASURA_GRAPHQL_STREAMING_QUERIES_MULTIPLEXED_REFETCH_INTERVAL=1000
HASURA_GRAPHQL_STRINGIFY_NUMERIC_TYPES=false
HASURA_GRAPHQL_CONNECTION_COMPRESSION=false
HASURA_GRAPHQL_WEBSOCKET_CONNECTION_INIT_TIMEOUT=3
HASURA_GRAPHQL_WEBSOCKET_KEEPALIVE=5
HASURA_GRAPHQL_WS_READ_COOKIE=false

HASURA_GRAPHQL_MAX_CACHE_SIZE=15 # MB - EE 版本

Hasura CLI

bash
curl -Lo hasura https://github.com/hasura/graphql-engine/releases/download/v2.19.0/cli-hasura-$(go env GOOS)-$(go env GOARCH)
chmod +x hasura
mv hasura ~/bin

mkdir ~/.hasura
echo '{"enable_telemetry": false,"show_update_notification": false}' > ~/.hasura/config.json

hasura init --endpoint http://localhost:8080 --admin-secret $TOKEN hasura
cd hasura
# http://localhost:9695/console
hasura console
.env
HASURA_GRAPHQL_ENDPOINT=
HASURA_GRAPHQL_ADMIN_SECRET=

HASURA_GRAPHQL_METADATA_DIRECTORY=
HASURA_GRAPHQL_ACTIONS_HANDLER_WEBHOOK_BASEURL=
text
📂 metadata
├─ 📂 databases
│  ├─ 📂 default
│  │  └─ 📂 tables
│  │     ├─ 📄 <schema>_<table>.yaml
│  │     └─ 📄 tables.yaml
│  └── 📄 databases.yaml
├─ 📄 actions.graphql
├─ 📄 actions.yaml
├─ 📄 allow_list.yaml
├─ 📄 api_limits.yaml
├─ 📄 cron_triggers.yaml
├─ 📄 graphql_schema_introspection.yaml
├─ 📄 inherited_roles.yaml
├─ 📄 network.yaml
├─ 📄 query_collections.yaml
├─ 📄 remote_schemas.yaml
├─ 📄 rest_endpoints.yaml
└─ 📄 version.yaml
📂 migrations
└─ 📂 default
   └─ 📂 <timestamp>_<name>
      ├─ 📄 down.sql
      └─ 📄 up.sql
📂 seeds
📄 config.yaml
bash
hasura migrate status

hasura migrate create "init" --from-server
hasura migrate apply --version 1550925483858 --type down --database-name <database-name>

hasura migrate squash --name AFTER --from FROM_VER --database-name DB

hasura migrate delete --all --server --database-name DB

Endpoints

EndpointAPIAccess
/v1/versionVersionPublic
/healthzHealthPublic
/v1/graphqlGraphQLPermission rules
/v1beta1/relayRelayPermission rules
/v1alpha1/graphqlLegacy GraphQLPermission rules
/api/restRestified GQLGQL REST Routes
/v2/querySchema - v2.0+Admin only
/v1/metadataMetadata - v2.0+Admin only
/v1/querySchema/MetadataAdmin only
/v1alpha1/pg_dumpPG DumpAdmin only
/v1alpha1/configConfigAdmin only
/v1/graphql/explainExplainAdmin only

Auth

Version

Hasura v2

  • 2021-02-23
  • 多数据源 - 同时连多个数据库
    • 相同数据库类型 Remote Join
  • 多数据库 - PG -> PG, MS SQL, MySQL
  • 支持 REST
    • REST -> GraphQL Query
  • 增强 AuthZ
  • HA
  • Metadata API

FAQ

"Query" has to be an object type

schema 变更流程

  1. 修改 SQL,调整 schema,alter table
  2. hasura md reload
  3. hasura md ic list - 确认是否有不一致的地方
  4. hasura md ic drop - 移除不一致的地方 - 注意也需要到 console 修复回来
  5. hasura md export - 更新 meta 到仓库
  6. pnpm graphql-codegen --config codegen.ts - 生成新的 graphql schema

关联信息

反向链接、本文链接的其他页面和外部资料。

参考资料

GitHub11 条
其他外链9 条
最近更新commit 403505a编辑