hasura-graphql-engine

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Hasura GraphQL Engine Mastery

Hasura GraphQL Engine 精通指南

A comprehensive skill for building production-ready GraphQL APIs with Hasura. Master instant API generation, granular permissions, authentication integration, event-driven architectures, custom business logic, and remote schema stitching for modern applications.
这是一份使用Hasura构建生产可用GraphQL API的全面指南。掌握即时API生成、细粒度权限、认证集成、事件驱动架构、自定义业务逻辑以及现代应用所需的远程模式拼接。

When to Use This Skill

何时使用该技能

Use Hasura GraphQL Engine when:
  • Building GraphQL APIs rapidly without writing backend code
  • Need instant CRUD APIs from existing PostgreSQL databases
  • Implementing granular row-level and column-level security
  • Building real-time applications with GraphQL subscriptions
  • Integrating multiple data sources (databases, REST APIs, GraphQL services)
  • Creating event-driven architectures with database triggers
  • Extending GraphQL with custom business logic via Actions
  • Implementing authentication and authorization at the API layer
  • Building admin panels, dashboards, or internal tools quickly
  • Migrating from REST to GraphQL without rewriting backend
  • Needing production-ready features (caching, rate limiting, monitoring)
  • Building multi-tenant SaaS applications with role-based access
在以下场景使用Hasura GraphQL Engine:
  • 无需编写后端代码,快速构建GraphQL API
  • 需要从现有PostgreSQL数据库生成即时CRUD API
  • 实现细粒度的行级和列级安全
  • 构建支持GraphQL订阅的实时应用
  • 集成多数据源(数据库、REST API、GraphQL服务)
  • 通过数据库触发器创建事件驱动架构
  • 通过Actions扩展GraphQL的自定义业务逻辑
  • 在API层实现认证与授权
  • 快速构建管理面板、仪表盘或内部工具
  • 无需重写后端,从REST迁移到GraphQL
  • 需要生产级功能(缓存、速率限制、监控)
  • 构建基于角色访问的多租户SaaS应用

Core Concepts

核心概念

Instant GraphQL API Generation

即时GraphQL API生成

Hasura's primary value proposition is automatic GraphQL API generation from your database schema:
  • Table Tracking: Point Hasura at PostgreSQL tables to instantly get queries, mutations, and subscriptions
  • Relationship Detection: Automatically infers foreign key relationships as GraphQL connections
  • Type Safety: Database schema translates directly to GraphQL types
  • Zero Code: No resolver writing, no ORM configuration, no boilerplate
  • Real-time by Default: Every query automatically has a subscription counterpart
How it works:
  1. Connect Hasura to your PostgreSQL database
  2. Track tables in the Hasura Console
  3. GraphQL API is immediately available with:
    • query
      - Fetch data with filtering, sorting, pagination
    • mutation
      - Insert, update, delete operations
    • subscription
      - Real-time data updates via WebSockets
Hasura的核心价值主张是从数据库架构自动生成GraphQL API
  • 表追踪:将Hasura指向PostgreSQL表,即可立即获得查询、变更和订阅能力
  • 关系检测:自动将外键关系推断为GraphQL连接
  • 类型安全:数据库架构直接转换为GraphQL类型
  • 零代码:无需编写解析器、配置ORM或编写样板代码
  • 默认实时:每个查询自动对应一个订阅版本
工作原理:
  1. 将Hasura连接到PostgreSQL数据库
  2. 在Hasura控制台中追踪表
  3. 立即获得可用的GraphQL API,包含:
    • query
      - 通过过滤、排序、分页获取数据
    • mutation
      - 插入、更新、删除操作
    • subscription
      - 通过WebSocket实现实时数据更新

Metadata-Driven Architecture

元数据驱动的架构

Hasura is metadata-driven, not code-driven:
  • Metadata: JSON/YAML configuration defining your API
  • Declarative: Define what you want, not how to implement it
  • Version Control: Metadata files can be committed to Git
  • CLI Migration: Hasura CLI manages metadata and migrations
  • Programmatic Control: Metadata API for automation
Key metadata components:
  • Table tracking and relationships
  • Permission rules
  • Remote schemas
  • Actions
  • Event triggers
  • Custom functions
Hasura是元数据驱动而非代码驱动:
  • 元数据:定义API的JSON/YAML配置
  • 声明式:定义你想要的结果,而非实现方式
  • 版本控制:元数据文件可提交至Git
  • CLI迁移:Hasura CLI管理元数据和迁移
  • 程序化控制:通过元数据API实现自动化
关键元数据组件:
  • 表追踪与关系
  • 权限规则
  • 远程模式
  • Actions
  • 事件触发器
  • 自定义函数

Permission System

权限系统

Hasura's permission system is its most powerful feature, enabling fine-grained access control:
  • Role-Based: Define permissions per GraphQL operation per role
  • Row-Level Security: Control which rows users can access
  • Column-Level Security: Hide sensitive columns from specific roles
  • Session Variables: Dynamic permissions based on JWT claims or webhook data
  • Check Constraints: Boolean expressions determining access
Permission Types:
  • select
    - Read permissions
  • insert
    - Create permissions
  • update
    - Modify permissions
  • delete
    - Remove permissions
Hasura的权限系统是其最强大的功能,支持细粒度访问控制:
  • 基于角色:为每个角色的GraphQL操作定义权限
  • 行级安全:控制用户可访问的行数据
  • 列级安全:对特定角色隐藏敏感列
  • 会话变量:基于JWT声明或Webhook数据的动态权限
  • 检查约束:决定访问权限的布尔表达式
权限类型:
  • select
    - 读取权限
  • insert
    - 创建权限
  • update
    - 修改权限
  • delete
    - 删除权限

Authentication Integration

认证集成

Hasura delegates authentication to your auth service but handles authorization:
  • JWT Mode: Validate JWT tokens containing user claims
  • Webhook Mode: Call webhook to get session variables
  • Session Variables:
    x-hasura-role
    ,
    x-hasura-user-id
    , custom claims
  • Multi-Provider: Support Auth0, Firebase, Cognito, custom auth
Auth Flow:
  1. User authenticates with your auth service (Auth0, Firebase, custom)
  2. Auth service issues JWT with Hasura claims
  3. Client sends JWT in Authorization header
  4. Hasura validates JWT and extracts session variables
  5. Permissions evaluated using session variables
  6. GraphQL query executed with appropriate access control
Hasura将认证委托给你的认证服务,但自行处理授权
  • JWT模式:验证包含用户声明的JWT令牌
  • Webhook模式:调用Webhook获取会话变量
  • 会话变量
    x-hasura-role
    x-hasura-user-id
    、自定义声明
  • 多提供商支持:支持Auth0、Firebase、Cognito、自定义认证服务
认证流程:
  1. 用户通过你的认证服务(Auth0、Firebase、自定义)完成认证
  2. 认证服务颁发包含Hasura声明的JWT
  3. 客户端在Authorization头中发送JWT
  4. Hasura验证JWT并提取会话变量
  5. 使用会话变量评估权限
  6. 执行带有相应访问控制的GraphQL查询

Event Triggers

权限系统深入解析

行级安全

Event Triggers enable event-driven architectures by invoking webhooks on database changes:
  • Database Events: INSERT, UPDATE, DELETE triggers
  • Reliable Delivery: At-least-once delivery with retries
  • Payload: Old and new row data in JSON
  • Async Processing: Long-running tasks, external integrations
  • Use Cases: Send emails, sync to Elasticsearch, update cache, trigger workflows
行级安全使用布尔检查表达式过滤可访问的行:
示例:用户仅能查看自己的数据
json
{
  "check": {
    "user_id": {
      "_eq": "X-Hasura-User-Id"
    }
  }
}
示例:多租户数据隔离
json
{
  "check": {
    "tenant_id": {
      "_eq": "X-Hasura-Tenant-Id"
    }
  }
}
示例:复杂访问规则
json
{
  "check": {
    "_or": [
      {
        "user_id": {
          "_eq": "X-Hasura-User-Id"
        }
      },
      {
        "is_public": {
          "_eq": true
        }
      }
    ]
  }
}

Actions

列级安全

Actions extend Hasura with custom business logic:
  • Custom Mutations: Define GraphQL mutations handled by your code
  • Custom Queries: Add custom query logic beyond database access
  • REST Integration: Call REST APIs from GraphQL
  • Type Safety: Define input/output types in GraphQL SDL
  • Handler: Your HTTP endpoint receives GraphQL variables
Common use cases:
  • Payment processing
  • Complex validations
  • Third-party API calls
  • Custom algorithms
  • File uploads
  • Email sending
控制每个角色可见的列:
示例:隐藏敏感用户字段
yaml
select:
  columns:
    - id
    - username
    - email
    # password_hash 被隐藏
    # created_at 被隐藏
示例:不同角色的不同视图
yaml
undefined

Remote Schemas

Admin角色查看所有列

Remote Schemas enable schema stitching by merging external GraphQL APIs:
  • Schema Stitching: Unify multiple GraphQL services
  • Type Extension: Extend types with fields from remote schemas
  • Permissions: Apply role-based permissions to remote schemas
  • Namespace: Isolate remote schemas to avoid conflicts
  • Use Cases: Microservices, legacy GraphQL APIs, third-party services
select: columns: "*"

Real-Time Subscriptions

User角色查看有限列

Hasura provides native GraphQL subscriptions:
  • Live Queries: Automatically push updates when data changes
  • WebSocket Protocol: Efficient bi-directional communication
  • Multiplexing: Optimize subscriptions for many concurrent clients
  • Filtering: Subscribe to specific subsets of data
  • Polling Fallback: HTTP-based streaming for restricted networks
select: columns: - id - username - profile_picture
undefined

Permission System Deep Dive

插入权限

Row-Level Security

Row-level security uses boolean check expressions to filter accessible rows:
Example: Users can only see their own data
json
{
  "check": {
    "user_id": {
      "_eq": "X-Hasura-User-Id"
    }
  }
}
Example: Multi-tenant data isolation
json
{
  "check": {
    "tenant_id": {
      "_eq": "X-Hasura-Tenant-Id"
    }
  }
}
Example: Complex access rules
json
{
  "check": {
    "_or": [
      {
        "user_id": {
          "_eq": "X-Hasura-User-Id"
        }
      },
      {
        "is_public": {
          "_eq": true
        }
      }
    ]
  }
}
控制可插入的数据:
示例:从会话中设置user_id
json
{
  "check": {
    "user_id": {
      "_eq": "X-Hasura-User-Id"
    }
  },
  "set": {
    "user_id": "X-Hasura-User-Id"
  }
}

Column-Level Security

更新权限

Control which columns are visible per role:
Example: Hide sensitive user fields
yaml
select:
  columns:
    - id
    - username
    - email
    # password_hash is hidden
    # created_at is hidden
Example: Different views for different roles
yaml
undefined
控制可更新的行和可设置的值:
示例:仅更新自己的数据
json
{
  "filter": {
    "user_id": {
      "_eq": "X-Hasura-User-Id"
    }
  },
  "check": {
    "user_id": {
      "_eq": "X-Hasura-User-Id"
    }
  },
  "set": {
    "updated_at": "now()"
  }
}
filter: 可选择用于更新的行 check: 更新完成后的验证 set: 自动设置列值

Admin role sees all columns

删除权限

select: columns: "*"
控制可删除的行:
示例:仅删除自己的数据
json
{
  "filter": {
    "user_id": {
      "_eq": "X-Hasura-User-Id"
    }
  }
}

User role sees limited columns

认证集成

JWT模式配置

select: columns: - id - username - profile_picture
undefined
配置Hasura验证JWT令牌:
环境变量:
bash
HASURA_GRAPHQL_JWT_SECRET='{
  "type": "RS256",
  "key": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}'
JWT声明结构:
json
{
  "sub": "user123",
  "iat": 1633024800,
  "exp": 1633111200,
  "https://hasura.io/jwt/claims": {
    "x-hasura-default-role": "user",
    "x-hasura-allowed-roles": ["user", "admin"],
    "x-hasura-user-id": "user123",
    "x-hasura-org-id": "org456"
  }
}
必填声明:
  • x-hasura-default-role
    : 请求未指定角色时的默认角色
  • x-hasura-allowed-roles
    : 用户可使用的角色数组
  • 自定义声明如
    x-hasura-user-id
    用于权限检查

Insert Permissions

Auth0集成

Control what data can be inserted:
Example: Set user_id from session
json
{
  "check": {
    "user_id": {
      "_eq": "X-Hasura-User-Id"
    }
  },
  "set": {
    "user_id": "X-Hasura-User-Id"
  }
}
Example: Validate ownership before insert
json
{
  "check": {
    "project": {
      "owner_id": {
        "_eq": "X-Hasura-User-Id"
      }
    }
  }
}
添加Hasura声明的Auth0规则:
javascript
function (user, context, callback) {
  const namespace = "https://hasura.io/jwt/claims";
  context.idToken[namespace] = {
    'x-hasura-default-role': 'user',
    'x-hasura-allowed-roles': ['user'],
    'x-hasura-user-id': user.user_id
  };
  callback(null, user, context);
}
客户端使用:
javascript
const token = await auth0Client.getTokenSilently();

const response = await fetch('https://my-hasura.app/v1/graphql', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ query, variables })
});

Update Permissions

Firebase集成

Control which rows can be updated and what values can be set:
Example: Update own data only
json
{
  "filter": {
    "user_id": {
      "_eq": "X-Hasura-User-Id"
    }
  },
  "check": {
    "user_id": {
      "_eq": "X-Hasura-User-Id"
    }
  },
  "set": {
    "updated_at": "now()"
  }
}
filter: Which rows can be selected for update check: Validation after update completes set: Automatically set column values
Firebase自定义声明:
javascript
// Admin SDK
const admin = require('firebase-admin');

async function setCustomClaims(uid) {
  await admin.auth().setCustomUserClaims(uid, {
    'https://hasura.io/jwt/claims': {
      'x-hasura-default-role': 'user',
      'x-hasura-allowed-roles': ['user'],
      'x-hasura-user-id': uid
    }
  });
}

Delete Permissions

Webhook模式

Control which rows can be deleted:
Example: Delete own data only
json
{
  "filter": {
    "user_id": {
      "_eq": "X-Hasura-User-Id"
    }
  }
}
替代JWT的方案 - Hasura为每个请求调用你的Webhook:
Webhook端点:
javascript
app.post('/auth-webhook', async (req, res) => {
  const authHeader = req.headers['authorization'];

  // 验证令牌(自定义逻辑)
  const user = await validateToken(authHeader);

  if (!user) {
    return res.status(401).json({ message: 'Unauthorized' });
  }

  // 返回会话变量
  res.json({
    'X-Hasura-User-Id': user.id,
    'X-Hasura-Role': user.role,
    'X-Hasura-Org-Id': user.orgId
  });
});
Hasura配置:
bash
HASURA_GRAPHQL_AUTH_HOOK=https://myapp.com/auth-webhook
HASURA_GRAPHQL_AUTH_HOOK_MODE=POST

Authentication Integration

事件触发器

JWT Mode Configuration

创建事件触发器

Configure Hasura to validate JWT tokens:
Environment Variable:
bash
HASURA_GRAPHQL_JWT_SECRET='{
  "type": "RS256",
  "key": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}'
JWT Claims Structure:
json
{
  "sub": "user123",
  "iat": 1633024800,
  "exp": 1633111200,
  "https://hasura.io/jwt/claims": {
    "x-hasura-default-role": "user",
    "x-hasura-allowed-roles": ["user", "admin"],
    "x-hasura-user-id": "user123",
    "x-hasura-org-id": "org456"
  }
}
Required Claims:
  • x-hasura-default-role
    : Default role if not specified in request
  • x-hasura-allowed-roles
    : Array of roles user can assume
  • Custom claims like
    x-hasura-user-id
    for permission checks
事件触发器在数据库变更时调用Webhook:
通过控制台创建:
  1. 导航到Events标签页
  2. 创建Trigger
  3. 选择表和操作(INSERT、UPDATE、DELETE)
  4. 提供Webhook URL
  5. 配置重试和超时设置
通过元数据API创建:
http
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "create_event_trigger",
  "args": {
    "name": "user_created",
    "table": {
      "name": "users",
      "schema": "public"
    },
    "webhook": "https://myapp.com/webhooks/user-created",
    "insert": {
      "columns": "*"
    },
    "retry_conf": {
      "num_retries": 3,
      "interval_sec": 10,
      "timeout_sec": 60
    }
  }
}

Auth0 Integration

事件负载结构

Auth0 Rule to add Hasura claims:
javascript
function (user, context, callback) {
  const namespace = "https://hasura.io/jwt/claims";
  context.idToken[namespace] = {
    'x-hasura-default-role': 'user',
    'x-hasura-allowed-roles': ['user'],
    'x-hasura-user-id': user.user_id
  };
  callback(null, user, context);
}
Client usage:
javascript
const token = await auth0Client.getTokenSilently();

const response = await fetch('https://my-hasura.app/v1/graphql', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${token}`,
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({ query, variables })
});
Webhook接收结构化JSON负载:
json
{
  "event": {
    "session_variables": {
      "x-hasura-role": "user",
      "x-hasura-user-id": "123"
    },
    "op": "INSERT",
    "data": {
      "old": null,
      "new": {
        "id": "uuid-here",
        "email": "user@example.com",
        "created_at": "2025-01-15T10:30:00Z"
      }
    }
  },
  "created_at": "2025-01-15T10:30:00.123456Z",
  "id": "event-id",
  "trigger": {
    "name": "user_created"
  },
  "table": {
    "schema": "public",
    "name": "users"
  }
}

Firebase Integration

事件触发器使用场景

Firebase custom claims:
javascript
// Admin SDK
const admin = require('firebase-admin');

async function setCustomClaims(uid) {
  await admin.auth().setCustomUserClaims(uid, {
    'https://hasura.io/jwt/claims': {
      'x-hasura-default-role': 'user',
      'x-hasura-allowed-roles': ['user'],
      'x-hasura-user-id': uid
    }
  });
}
发送欢迎邮件:
javascript
// Webhook处理器
app.post('/webhooks/user-created', async (req, res) => {
  const { event } = req.body;
  const user = event.data.new;

  await sendEmail({
    to: user.email,
    subject: '欢迎!',
    template: 'welcome',
    data: { name: user.name }
  });

  res.json({ success: true });
});
同步到Elasticsearch:
javascript
app.post('/webhooks/product-updated', async (req, res) => {
  const { event } = req.body;
  const product = event.data.new;

  await esClient.index({
    index: 'products',
    id: product.id,
    body: product
  });

  res.json({ success: true });
});

Webhook Mode

Actions(自定义业务逻辑)

定义Actions

Alternative to JWT - Hasura calls your webhook for each request:
Webhook endpoint:
javascript
app.post('/auth-webhook', async (req, res) => {
  const authHeader = req.headers['authorization'];

  // Validate token (your logic)
  const user = await validateToken(authHeader);

  if (!user) {
    return res.status(401).json({ message: 'Unauthorized' });
  }

  // Return session variables
  res.json({
    'X-Hasura-User-Id': user.id,
    'X-Hasura-Role': user.role,
    'X-Hasura-Org-Id': user.orgId
  });
});
Hasura config:
bash
HASURA_GRAPHQL_AUTH_HOOK=https://myapp.com/auth-webhook
HASURA_GRAPHQL_AUTH_HOOK_MODE=POST
Actions扩展GraphQL的自定义变更和查询:
GraphQL SDL定义:
graphql
type Mutation {
  login(username: String!, password: String!): LoginResponse
}

type LoginResponse {
  accessToken: String!
  refreshToken: String!
  user: User!
}
Action配置:
yaml
- name: login
  definition:
    kind: synchronous
    handler: https://myapp.com/actions/login
    forward_client_headers: true
    headers:
      - name: X-API-Key
        value: secret-key
  permissions:
    - role: anonymous

Event Triggers

Action处理器实现

Creating Event Triggers

Event triggers invoke webhooks on database changes:
Via Console:
  1. Navigate to Events tab
  2. Create Trigger
  3. Select table and operations (INSERT, UPDATE, DELETE)
  4. Provide webhook URL
  5. Configure retry and timeout settings
Via Metadata API:
http
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "create_event_trigger",
  "args": {
    "name": "user_created",
    "table": {
      "name": "users",
      "schema": "public"
    },
    "webhook": "https://myapp.com/webhooks/user-created",
    "insert": {
      "columns": "*"
    },
    "retry_conf": {
      "num_retries": 3,
      "interval_sec": 10,
      "timeout_sec": 60
    }
  }
}
Express.js处理器:
javascript
app.post('/actions/login', async (req, res) => {
  const { input, session_variables } = req.body;
  const { username, password } = input;

  // 验证凭证
  const user = await validateCredentials(username, password);

  if (!user) {
    return res.status(401).json({
      message: '无效凭证'
    });
  }

  // 生成令牌
  const accessToken = generateJWT(user);
  const refreshToken = generateRefreshToken(user);

  // 返回Action响应
  res.json({
    accessToken,
    refreshToken,
    user: {
      id: user.id,
      username: user.username,
      email: user.email
    }
  });
});

Event Payload Structure

远程模式

添加远程模式

Webhook receives structured JSON payload:
json
{
  "event": {
    "session_variables": {
      "x-hasura-role": "user",
      "x-hasura-user-id": "123"
    },
    "op": "INSERT",
    "data": {
      "old": null,
      "new": {
        "id": "uuid-here",
        "email": "user@example.com",
        "created_at": "2025-01-15T10:30:00Z"
      }
    }
  },
  "created_at": "2025-01-15T10:30:00.123456Z",
  "id": "event-id",
  "trigger": {
    "name": "user_created"
  },
  "table": {
    "schema": "public",
    "name": "users"
  }
}
集成外部GraphQL API:
通过元数据API添加:
http
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "add_remote_schema",
  "args": {
    "name": "auth0_api",
    "definition": {
      "url": "https://myapp.auth0.com/graphql",
      "headers": [
        {
          "name": "Authorization",
          "value": "Bearer ${AUTH0_TOKEN}"
        }
      ],
      "forward_client_headers": false,
      "timeout_seconds": 60
    }
  }
}

Event Trigger Use Cases

远程模式权限

Send Welcome Email:
javascript
// Webhook handler
app.post('/webhooks/user-created', async (req, res) => {
  const { event } = req.body;
  const user = event.data.new;

  await sendEmail({
    to: user.email,
    subject: 'Welcome!',
    template: 'welcome',
    data: { name: user.name }
  });

  res.json({ success: true });
});
Sync to Elasticsearch:
javascript
app.post('/webhooks/product-updated', async (req, res) => {
  const { event } = req.body;
  const product = event.data.new;

  await esClient.index({
    index: 'products',
    id: product.id,
    body: product
  });

  res.json({ success: true });
});
Trigger Workflow:
javascript
app.post('/webhooks/order-placed', async (req, res) => {
  const { event } = req.body;
  const order = event.data.new;

  // Trigger payment processing
  await processPayment(order.id);

  // Notify inventory system
  await updateInventory(order.items);

  // Send confirmation email
  await sendOrderConfirmation(order);

  res.json({ success: true });
});
为远程模式应用基于角色的权限:
原始远程模式:
graphql
type User {
  id: ID!
  first_name: String!
  last_name: String!
  phone: String!
  email: String!
}

type Query {
  user(id: ID!): User
  get_users_by_name(first_name: String!, last_name: String): [User]
}
'public'角色的受限模式:
graphql
type User {
  first_name: String!
  last_name: String!
}

type Query {
  get_users_by_name(first_name: String!, last_name: String): [User]
}

Actions (Custom Business Logic)

生产环境部署

Defining Actions

Docker部署

Actions extend GraphQL with custom mutations and queries:
GraphQL SDL Definition:
graphql
type Mutation {
  login(username: String!, password: String!): LoginResponse
}

type LoginResponse {
  accessToken: String!
  refreshToken: String!
  user: User!
}
Action Configuration:
yaml
- name: login
  definition:
    kind: synchronous
    handler: https://myapp.com/actions/login
    forward_client_headers: true
    headers:
      - name: X-API-Key
        value: secret-key
  permissions:
    - role: anonymous
docker-compose.yml:
yaml
version: '3.8'

services:
  postgres:
    image: postgres:15
    restart: always
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: postgrespassword
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  hasura:
    image: hasura/graphql-engine:v2.36.0
    ports:
      - "8080:8080"
    depends_on:
      postgres:
        condition: service_healthy
    restart: always
    environment:
      HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
      HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
      HASURA_GRAPHQL_DEV_MODE: "true"
      HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
      HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey
      HASURA_GRAPHQL_JWT_SECRET: '{"type":"HS256","key":"super-secret-jwt-signing-key-min-32-chars"}'
      HASURA_GRAPHQL_UNAUTHORIZED_ROLE: anonymous

volumes:
  db_data:

Action Handler Implementation

Kubernetes部署

Express.js Handler:
javascript
app.post('/actions/login', async (req, res) => {
  const { input, session_variables } = req.body;
  const { username, password } = input;

  // Validate credentials
  const user = await validateCredentials(username, password);

  if (!user) {
    return res.status(401).json({
      message: 'Invalid credentials'
    });
  }

  // Generate tokens
  const accessToken = generateJWT(user);
  const refreshToken = generateRefreshToken(user);

  // Return action response
  res.json({
    accessToken,
    refreshToken,
    user: {
      id: user.id,
      username: user.username,
      email: user.email
    }
  });
});
hasura-deployment.yaml:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hasura
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hasura
  template:
    metadata:
      labels:
        app: hasura
    spec:
      containers:
      - name: hasura
        image: hasura/graphql-engine:v2.36.0
        ports:
        - containerPort: 8080
        env:
        - name: HASURA_GRAPHQL_DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: hasura-secrets
              key: database-url
        - name: HASURA_GRAPHQL_ADMIN_SECRET
          valueFrom:
            secretKeyRef:
              name: hasura-secrets
              key: admin-secret
        - name: HASURA_GRAPHQL_JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: hasura-secrets
              key: jwt-secret
        - name: HASURA_GRAPHQL_ENABLE_CONSOLE
          value: "false"
        - name: HASURA_GRAPHQL_ENABLE_TELEMETRY
          value: "false"
        resources:
          requests:
            memory: "256Mi"
            cpu: "100m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: hasura
  namespace: production
spec:
  type: ClusterIP
  selector:
    app: hasura
  ports:
  - port: 80
    targetPort: 8080

Action Permissions

环境变量(生产环境)

Control which roles can execute actions:
Via Metadata API:
http
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "create_action_permission",
  "args": {
    "action": "insertAuthor",
    "role": "user"
  }
}
Multiple Roles:
yaml
permissions:
  - role: user
  - role: admin
  - role: anonymous
必备生产配置:
bash
undefined

Action Types

数据库

Synchronous Actions:
  • Client waits for response
  • Use for: Login, payments, validations
  • Timeout: Configurable (default 30s)
Asynchronous Actions:
  • Returns immediately with action ID
  • Use for: Long-running tasks, batch processing
  • Poll for completion or use webhooks
HASURA_GRAPHQL_DATABASE_URL=postgres://user:password@host:5432/dbname

Advanced Action Patterns

安全

Payment Processing:
graphql
type Mutation {
  processPayment(
    orderId: ID!
    amount: Float!
    currency: String!
    paymentMethod: String!
  ): PaymentResponse
}

type PaymentResponse {
  success: Boolean!
  transactionId: String
  error: String
}
File Upload:
graphql
type Mutation {
  uploadFile(
    file: String!  # Base64 encoded
    fileName: String!
    mimeType: String!
  ): FileUploadResponse
}

type FileUploadResponse {
  url: String!
  fileId: ID!
}
Complex Validation:
graphql
type Mutation {
  createProject(
    name: String!
    description: String!
    teamMembers: [ID!]!
  ): CreateProjectResponse
}

type CreateProjectResponse {
  project: Project
  errors: [ValidationError!]
}

type ValidationError {
  field: String!
  message: String!
}
HASURA_GRAPHQL_ADMIN_SECRET=strong-random-secret HASURA_GRAPHQL_JWT_SECRET='{"type":"RS256","key":"..."}' HASURA_GRAPHQL_UNAUTHORIZED_ROLE=anonymous

Remote Schemas

性能

Adding Remote Schemas

Integrate external GraphQL APIs:
Via Metadata API:
http
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "add_remote_schema",
  "args": {
    "name": "auth0_api",
    "definition": {
      "url": "https://myapp.auth0.com/graphql",
      "headers": [
        {
          "name": "Authorization",
          "value": "Bearer ${AUTH0_TOKEN}"
        }
      ],
      "forward_client_headers": false,
      "timeout_seconds": 60
    }
  }
}
HASURA_GRAPHQL_ENABLE_CONSOLE=false HASURA_GRAPHQL_DEV_MODE=false HASURA_GRAPHQL_ENABLE_TELEMETRY=false

Remote Schema Customization

日志

Customize type and field names to avoid conflicts:
http
{
  "type": "add_remote_schema",
  "args": {
    "name": "countries",
    "definition": {
      "url": "https://countries.trevorblades.com/graphql",
      "customization": {
        "root_fields_namespace": "countries_api",
        "type_names": {
          "prefix": "Countries_",
          "suffix": "_Type"
        },
        "field_names": [
          {
            "parent_type": "Country",
            "prefix": "country_"
          }
        ]
      }
    }
  }
}
HASURA_GRAPHQL_ENABLED_LOG_TYPES=startup,http-log,webhook-log,websocket-log

Remote Schema Permissions

速率限制

Apply role-based permissions to remote schemas:
Original Remote Schema:
graphql
type User {
  id: ID!
  first_name: String!
  last_name: String!
  phone: String!
  email: String!
}

type Query {
  user(id: ID!): User
  get_users_by_name(first_name: String!, last_name: String): [User]
}
Restricted Schema for 'public' Role:
graphql
type User {
  first_name: String!
  last_name: String!
}

type Query {
  get_users_by_name(first_name: String!, last_name: String): [User]
}
Via Metadata API:
http
POST /v1/metadata HTTP/1.1
Content-Type: application/json
X-Hasura-Role: admin

{
  "type": "add_remote_schema_permissions",
  "args": {
    "remote_schema": "user_api",
    "role": "public",
    "definition": {
      "schema": "type User { first_name: String! last_name: String! } type Query { get_users_by_name(first_name: String!, last_name: String): [User] }"
    }
  }
}
HASURA_GRAPHQL_RATE_LIMIT_PER_MINUTE=1000

Remote Schema Argument Presets

CORS

Automatically inject session variables into remote schema queries:
Session Variable Preset:
graphql
type Query {
  get_user(id: ID! @preset(value: "x-hasura-user-id")): User
  get_user_activities(user_id: ID!, limit: Int!): [Activity]
}
Static Value Preset:
graphql
type Query {
  get_user(id: ID! @preset(value: "x-hasura-user-id")): User
  get_user_activities(
    user_id: ID!
    limit: Int! @preset(value: 10)
  ): [Activity]
}
Literal String (not session variable):
graphql
type Query {
  hello(text: String! @preset(value: "x-hasura-hello", static: true))
}

Remote Relationships

连接

Connect local database tables to remote schemas:
Example: Link local customer to remote payments API
SQL Table:
sql
CREATE TABLE customer (
  id SERIAL PRIMARY KEY,
  name TEXT NOT NULL
);
Remote Schema (Payments API):
graphql
type Transaction {
  customer_id: Int!
  amount: Int!
  time: String!
  merchant: String!
}

type Query {
  transactions(customer_id: String!, limit: Int): [Transaction]
}
Remote Relationship Definition:
yaml
- table:
    name: customer
    schema: public
  remote_relationships:
    - name: customer_transactions_history
      definition:
        remote_schema: payments
        hasura_fields:
          - id
        remote_field:
          transactions:
            arguments:
              customer_id: $id
GraphQL Query with Remote Relationship:
graphql
query {
  customer {
    name
    customer_transactions_history {
      amount
      time
    }
  }
}
HASURA_GRAPHQL_PG_CONNECTIONS=50 HASURA_GRAPHQL_PG_TIMEOUT=60
undefined

Production Deployment

迁移与版本控制

Docker Deployment

Hasura CLI设置

docker-compose.yml:
yaml
version: '3.8'

services:
  postgres:
    image: postgres:15
    restart: always
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      POSTGRES_PASSWORD: postgrespassword
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U postgres"]
      interval: 10s
      timeout: 5s
      retries: 5

  hasura:
    image: hasura/graphql-engine:v2.36.0
    ports:
      - "8080:8080"
    depends_on:
      postgres:
        condition: service_healthy
    restart: always
    environment:
      HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
      HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
      HASURA_GRAPHQL_DEV_MODE: "true"
      HASURA_GRAPHQL_ENABLED_LOG_TYPES: startup, http-log, webhook-log, websocket-log, query-log
      HASURA_GRAPHQL_ADMIN_SECRET: myadminsecretkey
      HASURA_GRAPHQL_JWT_SECRET: '{"type":"HS256","key":"super-secret-jwt-signing-key-min-32-chars"}'
      HASURA_GRAPHQL_UNAUTHORIZED_ROLE: anonymous

volumes:
  db_data:
初始化Hasura项目:
bash
hasura init my-project --endpoint https://hasura.myapp.com
cd my-project
项目结构:
my-project/
├── config.yaml              # Hasura CLI配置
├── metadata/                # 元数据文件
│   ├── databases/
│   │   └── default/
│   │       ├── tables/
│   │       │   ├── public_users.yaml
│   │       │   └── public_posts.yaml
│   ├── actions.yaml
│   ├── remote_schemas.yaml
│   └── version.yaml
└── migrations/              # 数据库迁移
    └── default/
        ├── 1642531200000_create_users_table/
        │   └── up.sql
        └── 1642531300000_create_posts_table/
            └── up.sql

Kubernetes Deployment

最佳实践

安全最佳实践

hasura-deployment.yaml:
yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: hasura
  namespace: production
spec:
  replicas: 3
  selector:
    matchLabels:
      app: hasura
  template:
    metadata:
      labels:
        app: hasura
    spec:
      containers:
      - name: hasura
        image: hasura/graphql-engine:v2.36.0
        ports:
        - containerPort: 8080
        env:
        - name: HASURA_GRAPHQL_DATABASE_URL
          valueFrom:
            secretKeyRef:
              name: hasura-secrets
              key: database-url
        - name: HASURA_GRAPHQL_ADMIN_SECRET
          valueFrom:
            secretKeyRef:
              name: hasura-secrets
              key: admin-secret
        - name: HASURA_GRAPHQL_JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: hasura-secrets
              key: jwt-secret
        - name: HASURA_GRAPHQL_ENABLE_CONSOLE
          value: "false"
        - name: HASURA_GRAPHQL_ENABLE_TELEMETRY
          value: "false"
        resources:
          requests:
            memory: "256Mi"
            cpu: "100m"
          limits:
            memory: "512Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: hasura
  namespace: production
spec:
  type: ClusterIP
  selector:
    app: hasura
  ports:
  - port: 80
    targetPort: 8080
  1. 生产环境始终使用ADMIN_SECRET
    • 绝不暴露未认证的管理API
    • 定期轮换密钥
    • 使用强随机密钥(至少32个字符)
  2. 实现正确的JWT验证
    • 生产环境使用RS256(非对称加密)
    • 设置合适的令牌过期时间
    • 验证发行者和受众声明
  3. 应用最小权限原则
    • 从无访问权限开始,按需添加权限
    • 为所有表使用行级安全
    • 对未授权角色隐藏敏感列

Environment Variables (Production)

性能最佳实践

Essential Production Config:
bash
undefined
  1. 优化数据库查询
    • 创建合适的索引
    • 为复杂查询使用数据库视图
    • 利用PostgreSQL性能调优
  2. 使用查询缓存
    • 为昂贵的查询启用@cached指令
    • 设置合适的TTL值
    • 尽可能在CDN层缓存

Database

常见模式与示例

模式1:多租户SaaS

HASURA_GRAPHQL_DATABASE_URL=postgres://user:password@host:5432/dbname
架构:
sql
CREATE TABLE organizations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL
);

CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT NOT NULL UNIQUE,
  organization_id UUID NOT NULL REFERENCES organizations(id)
);
权限(users表):
json
{
  "filter": {
    "organization_id": {
      "_eq": "X-Hasura-Org-Id"
    }
  }
}

Security

模式2:社交媒体应用

HASURA_GRAPHQL_ADMIN_SECRET=strong-random-secret HASURA_GRAPHQL_JWT_SECRET='{"type":"RS256","key":"..."}' HASURA_GRAPHQL_UNAUTHORIZED_ROLE=anonymous
架构:
sql
CREATE TABLE users (
  id UUID PRIMARY KEY,
  username TEXT UNIQUE NOT NULL,
  bio TEXT,
  avatar_url TEXT
);

CREATE TABLE posts (
  id UUID PRIMARY KEY,
  user_id UUID REFERENCES users(id),
  content TEXT NOT NULL,
  is_public BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW()
);
权限:查看帖子(用户可查看自己的帖子、公开帖子以及关注用户的帖子):
json
{
  "filter": {
    "_or": [
      {
        "user_id": {
          "_eq": "X-Hasura-User-Id"
        }
      },
      {
        "is_public": {
          "_eq": true
        }
      },
      {
        "user": {
          "followers": {
            "follower_id": {
              "_eq": "X-Hasura-User-Id"
            }
          }
        }
      }
    ]
  }
}

Performance

故障排除

常见问题与解决方案

HASURA_GRAPHQL_ENABLE_CONSOLE=false HASURA_GRAPHQL_DEV_MODE=false HASURA_GRAPHQL_ENABLE_TELEMETRY=false
问题:JWT验证失败
解决方案:
1. 验证JWT密钥配置与认证提供商匹配
2. 检查JWT包含所需的Hasura声明
3. 确保声明在正确的命名空间(https://hasura.io/jwt/claims)
4. 验证JWT未过期
问题:权限拒绝错误
解决方案:
1. 检查角色在allowed_roles中
2. 验证权限规则允许该操作
3. 使用admin角色测试以隔离权限问题
4. 检查会话变量是否正确发送

Logging

额外资源

官方文档

HASURA_GRAPHQL_ENABLED_LOG_TYPES=startup,http-log,webhook-log,websocket-log

Rate Limiting

学习资源

HASURA_GRAPHQL_RATE_LIMIT_PER_MINUTE=1000

技能版本: 1.0.0 最后更新: 2025年1月 技能分类: 后端、GraphQL、API开发、实时、数据库 兼容环境: PostgreSQL、Auth0、Firebase、Cognito、Kubernetes、Docker

CORS

Connections

HASURA_GRAPHQL_PG_CONNECTIONS=50 HASURA_GRAPHQL_PG_TIMEOUT=60
undefined

Monitoring and Observability

Health Check Endpoint:
bash
curl http://hasura:8080/healthz

Returns: OK


**Prometheus Metrics:**
```bash
HASURA_GRAPHQL_ENABLE_METRICS=true
HASURA_GRAPHQL_METRICS_SECRET=metrics-secret

**Structured Logging:**
```bash
HASURA_GRAPHQL_ENABLED_LOG_TYPES=startup,http-log,webhook-log,websocket-log,query-log
HASURA_GRAPHQL_LOG_LEVEL=info
APM Integration (Datadog example):
yaml
env:
  - name: HASURA_GRAPHQL_ENABLE_APM
    value: "true"
  - name: DD_AGENT_HOST
    valueFrom:
      fieldRef:
        fieldPath: status.hostIP
  - name: DD_SERVICE
    value: "hasura-graphql"
  - name: DD_ENV
    value: "production"

Migrations and Version Control

Hasura CLI Setup

Initialize Hasura project:
bash
hasura init my-project --endpoint https://hasura.myapp.com
cd my-project
Project structure:
my-project/
├── config.yaml              # Hasura CLI config
├── metadata/                # Metadata files
│   ├── databases/
│   │   └── default/
│   │       ├── tables/
│   │       │   ├── public_users.yaml
│   │       │   └── public_posts.yaml
│   ├── actions.yaml
│   ├── remote_schemas.yaml
│   └── version.yaml
└── migrations/              # Database migrations
    └── default/
        ├── 1642531200000_create_users_table/
        │   └── up.sql
        └── 1642531300000_create_posts_table/
            └── up.sql

Creating Migrations

Via Console (auto-tracked):
bash
undefined

Start console with migration tracking

hasura console

Make changes in console UI

Migrations auto-generated in migrations/ folder


**Manual migration:**
```bash

Create migration

hasura migrate create create_users_table --database-name default

Edit generated SQL files

migrations/default/{timestamp}_create_users_table/up.sql

migrations/default/{timestamp}_create_users_table/down.sql


**Example migration (up.sql):**
```sql
CREATE TABLE public.users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT NOT NULL UNIQUE,
  username TEXT NOT NULL UNIQUE,
  created_at TIMESTAMP NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);

CREATE INDEX idx_users_email ON public.users(email);
CREATE INDEX idx_users_username ON public.users(username);
Example migration (down.sql):
sql
DROP TABLE IF EXISTS public.users CASCADE;

Applying Migrations

Apply migrations:
bash
undefined

Apply all pending migrations

hasura migrate apply --database-name default

Apply specific version

hasura migrate apply --version 1642531200000 --database-name default

Check migration status

hasura migrate status --database-name default
undefined

Exporting and Importing Metadata

Export metadata:
bash
hasura metadata export

Exports to metadata/ folder


**Apply metadata:**
```bash
hasura metadata apply

Applies metadata from metadata/ folder


**Reload metadata:**
```bash
hasura metadata reload

CI/CD Integration

GitHub Actions example:
yaml
name: Deploy Hasura

on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      - name: Install Hasura CLI
        run: |
          curl -L https://github.com/hasura/graphql-engine/raw/stable/cli/get.sh | bash

      - name: Apply Migrations
        env:
          HASURA_GRAPHQL_ENDPOINT: ${{ secrets.HASURA_ENDPOINT }}
          HASURA_GRAPHQL_ADMIN_SECRET: ${{ secrets.HASURA_ADMIN_SECRET }}
        run: |
          cd hasura
          hasura migrate apply --database-name default
          hasura metadata apply

      - name: Reload Metadata
        env:
          HASURA_GRAPHQL_ENDPOINT: ${{ secrets.HASURA_ENDPOINT }}
          HASURA_GRAPHQL_ADMIN_SECRET: ${{ secrets.HASURA_ADMIN_SECRET }}
        run: |
          cd hasura
          hasura metadata reload

Best Practices

Security Best Practices

  1. Always use ADMIN_SECRET in production
    • Never expose admin API without authentication
    • Rotate secrets regularly
    • Use strong, random secrets (min 32 characters)
  2. Implement proper JWT validation
    • Use RS256 (asymmetric) in production
    • Set appropriate token expiration
    • Validate issuer and audience claims
  3. Apply least-privilege permissions
    • Start with no access, add permissions as needed
    • Use row-level security for all tables
    • Hide sensitive columns from unauthorized roles
  4. Disable console in production
    • HASURA_GRAPHQL_ENABLE_CONSOLE=false
    • Use metadata files and CLI for changes
  5. Enable rate limiting
    • Protect against DoS attacks
    • Set per-role limits if needed
    • Monitor and adjust based on usage
  6. Validate webhook payloads
    • Use webhook secrets for event triggers
    • Validate action inputs
    • Sanitize all user inputs

Performance Best Practices

  1. Optimize database queries
    • Create appropriate indexes
    • Use database views for complex queries
    • Leverage PostgreSQL performance tuning
  2. Use query caching
    • Enable @cached directive for expensive queries
    • Set appropriate TTL values
    • Cache at CDN level when possible
  3. Limit query depth and complexity
    • Set max query depth limits
    • Restrict deeply nested queries
    • Use pagination for large result sets
  4. Configure connection pooling
    • Tune
      HASURA_GRAPHQL_PG_CONNECTIONS
    • Monitor connection usage
    • Use PgBouncer for large deployments
  5. Optimize subscriptions
    • Use subscription multiplexing
    • Limit concurrent subscriptions per client
    • Consider polling for less time-sensitive data

Development Workflow Best Practices

  1. Version control metadata
    • Commit metadata/ folder to Git
    • Use migrations for all schema changes
    • Review metadata changes in PRs
  2. Environment separation
    • Development, staging, production environments
    • Use different admin secrets per environment
    • Test migrations in staging first
  3. Testing strategy
    • Test permissions thoroughly
    • Integration test event triggers
    • Test action handlers independently
  4. Documentation
    • Document custom actions and their inputs/outputs
    • Explain complex permission rules
    • Maintain API documentation for consumers
  5. Monitoring and alerting
    • Monitor query performance
    • Alert on failed webhooks/event triggers
    • Track error rates and latencies

Common Patterns and Examples

Pattern 1: Multi-Tenant SaaS

Schema:
sql
CREATE TABLE organizations (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL
);

CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email TEXT NOT NULL UNIQUE,
  organization_id UUID NOT NULL REFERENCES organizations(id)
);

CREATE TABLE projects (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  name TEXT NOT NULL,
  organization_id UUID NOT NULL REFERENCES organizations(id)
);
Permissions (users table):
json
{
  "filter": {
    "organization_id": {
      "_eq": "X-Hasura-Org-Id"
    }
  }
}
JWT Claims:
json
{
  "https://hasura.io/jwt/claims": {
    "x-hasura-default-role": "user",
    "x-hasura-allowed-roles": ["user", "org-admin"],
    "x-hasura-user-id": "user-uuid",
    "x-hasura-org-id": "org-uuid"
  }
}

Pattern 2: Social Media Application

Schema:
sql
CREATE TABLE users (
  id UUID PRIMARY KEY,
  username TEXT UNIQUE NOT NULL,
  bio TEXT,
  avatar_url TEXT
);

CREATE TABLE posts (
  id UUID PRIMARY KEY,
  user_id UUID REFERENCES users(id),
  content TEXT NOT NULL,
  is_public BOOLEAN DEFAULT true,
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE follows (
  follower_id UUID REFERENCES users(id),
  following_id UUID REFERENCES users(id),
  PRIMARY KEY (follower_id, following_id)
);

CREATE TABLE likes (
  user_id UUID REFERENCES users(id),
  post_id UUID REFERENCES posts(id),
  PRIMARY KEY (user_id, post_id)
);
Permission: View posts (user can see own posts, public posts, and posts from followed users):
json
{
  "filter": {
    "_or": [
      {
        "user_id": {
          "_eq": "X-Hasura-User-Id"
        }
      },
      {
        "is_public": {
          "_eq": true
        }
      },
      {
        "user": {
          "followers": {
            "follower_id": {
              "_eq": "X-Hasura-User-Id"
            }
          }
        }
      }
    ]
  }
}

Pattern 3: E-Commerce Platform

Schema:
sql
CREATE TABLE products (
  id UUID PRIMARY KEY,
  name TEXT NOT NULL,
  price DECIMAL(10,2) NOT NULL,
  stock_quantity INT NOT NULL,
  is_active BOOLEAN DEFAULT true
);

CREATE TABLE orders (
  id UUID PRIMARY KEY,
  user_id UUID NOT NULL,
  status TEXT NOT NULL,
  total DECIMAL(10,2) NOT NULL,
  created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE order_items (
  id UUID PRIMARY KEY,
  order_id UUID REFERENCES orders(id),
  product_id UUID REFERENCES products(id),
  quantity INT NOT NULL,
  price DECIMAL(10,2) NOT NULL
);
Event Trigger: Order confirmation email
javascript
app.post('/webhooks/order-created', async (req, res) => {
  const { event } = req.body;
  const order = event.data.new;

  // Fetch order details with items
  const orderDetails = await fetchOrderDetails(order.id);

  // Send confirmation email
  await sendEmail({
    to: orderDetails.user.email,
    template: 'order-confirmation',
    data: orderDetails
  });

  res.json({ success: true });
});
Action: Process payment
graphql
type Mutation {
  processPayment(
    orderId: ID!
    paymentMethodId: String!
  ): PaymentResponse
}

type PaymentResponse {
  success: Boolean!
  orderId: ID!
  transactionId: String
  error: String
}

Pattern 4: Real-Time Collaboration

Schema:
sql
CREATE TABLE documents (
  id UUID PRIMARY KEY,
  title TEXT NOT NULL,
  content JSONB NOT NULL DEFAULT '{}'::jsonb,
  owner_id UUID NOT NULL,
  updated_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE document_collaborators (
  document_id UUID REFERENCES documents(id),
  user_id UUID NOT NULL,
  permission TEXT NOT NULL, -- 'read', 'write', 'admin'
  PRIMARY KEY (document_id, user_id)
);
Permission: Access documents (own or collaborated):
json
{
  "filter": {
    "_or": [
      {
        "owner_id": {
          "_eq": "X-Hasura-User-Id"
        }
      },
      {
        "collaborators": {
          "user_id": {
            "_eq": "X-Hasura-User-Id"
          }
        }
      }
    ]
  }
}
GraphQL Subscription: Real-time updates
graphql
subscription DocumentUpdates($documentId: uuid!) {
  documents_by_pk(id: $documentId) {
    id
    title
    content
    updated_at
  }
}

Pattern 5: Admin Dashboard with Analytics

Custom SQL Function for analytics:
sql
CREATE OR REPLACE FUNCTION get_user_stats(user_row users)
RETURNS TABLE (
  total_posts INT,
  total_followers INT,
  total_following INT,
  engagement_rate DECIMAL
) AS $$
  SELECT
    (SELECT COUNT(*) FROM posts WHERE user_id = user_row.id)::INT,
    (SELECT COUNT(*) FROM follows WHERE following_id = user_row.id)::INT,
    (SELECT COUNT(*) FROM follows WHERE follower_id = user_row.id)::INT,
    (SELECT AVG(like_count) FROM posts WHERE user_id = user_row.id)::DECIMAL
$$ LANGUAGE SQL STABLE;
Track function in Hasura:
yaml
- function:
    name: get_user_stats
    schema: public
  configuration:
    custom_root_fields:
      function: getUserStats
GraphQL Query:
graphql
query UserWithStats {
  users {
    id
    username
    get_user_stats {
      total_posts
      total_followers
      total_following
      engagement_rate
    }
  }
}

Troubleshooting

Common Issues and Solutions

Issue: JWT validation failing
Solution:
1. Verify JWT secret configuration matches your auth provider
2. Check JWT contains required Hasura claims
3. Ensure claims are in correct namespace (https://hasura.io/jwt/claims)
4. Validate JWT hasn't expired
5. Check issuer and audience if configured
Issue: Permission denied errors
Solution:
1. Check role is in allowed_roles
2. Verify permission rules allow the operation
3. Test with admin role to isolate permission issue
4. Check session variables are being sent correctly
5. Review both row-level and column-level permissions
Issue: Event trigger not firing
Solution:
1. Check webhook is accessible from Hasura
2. Verify table name and operation match trigger config
3. Check webhook returns 200 status
4. Review event trigger logs in Hasura console
5. Ensure database triggers are enabled
Issue: Action returning errors
Solution:
1. Verify action handler URL is accessible
2. Check request/response format matches action definition
3. Review action handler logs
4. Test action handler independently
5. Verify permissions allow the role to execute action
Issue: Remote schema not loading
Solution:
1. Verify remote GraphQL endpoint is accessible
2. Check authentication headers if required
3. Test remote schema independently
4. Review timeout settings
5. Check for type name conflicts
Issue: Subscription connection dropping
Solution:
1. Check WebSocket support on hosting platform
2. Verify connection timeout settings
3. Implement reconnection logic in client
4. Check for firewall/proxy blocking WebSockets
5. Monitor connection pool limits

Additional Resources

Official Documentation

Learning Resources

Community

Tools and Integrations


Skill Version: 1.0.0 Last Updated: January 2025 Skill Category: Backend, GraphQL, API Development, Real-time, Database Compatible With: PostgreSQL, Auth0, Firebase, Cognito, Kubernetes, Docker