Skip to main content

Hasura

  • hasura/graphql-engine
    • 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_cataloghdb_view
  • HGE - Hasura GraphQL Engine
info
# 创建一个数据库
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
-- 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

{
// 加密算法 - 例如 对称加密 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 内容

{
"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
    • 允许的角色 - 自定义 - 对应后台配置的权限
  • https://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 角色
{ "user_id": { "_eq": "X-Hasura-User-Id" } }

中间关系

{
"_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" } } }
]
}

继承

{
"_exists": {
"_table": {
"table": "flattened_user_roles",
"schema": "public"
},
"_where": {
"_and": [{ "user_id": { "_eq": "X-Hasura-User-Id" } }, { "role_id": { "_eq": "manager" } }]
}
}
}
  • flattened_user_roles 为视图
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

# 应用相关
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

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=
📂 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
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