hasura-graphql-engine

安装量: 70
排名: #11003

安装

npx skills add https://github.com/manutej/luxor-claude-marketplace --skill hasura-graphql-engine
Hasura GraphQL Engine Mastery
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.
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
Core Concepts
Instant GraphQL API Generation
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:
Connect Hasura to your PostgreSQL database
Track tables in the Hasura Console
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
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
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
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:
User authenticates with your auth service (Auth0, Firebase, custom)
Auth service issues JWT with Hasura claims
Client sends JWT in Authorization header
Hasura validates JWT and extracts session variables
Permissions evaluated using session variables
GraphQL query executed with appropriate access control
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
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
Remote Schemas
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
Real-Time Subscriptions
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 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 { "check" : { "user_id" : { "_eq" : "X-Hasura-User-Id" } } } Example: Multi-tenant data isolation { "check" : { "tenant_id" : { "_eq" : "X-Hasura-Tenant-Id" } } } Example: Complex access rules { "check" : { "_or" : [ { "user_id" : { "_eq" : "X-Hasura-User-Id" } } , { "is_public" : { "_eq" : true } } ] } } Column-Level Security Control which columns are visible per role: Example: Hide sensitive user fields select : columns : - id - username - email

password_hash is hidden

created_at is hidden

Example: Different views for different roles

Admin role sees all columns

select : columns : "*"

User role sees limited columns

select
:
columns
:
-
id
-
username
-
profile_picture
Insert Permissions
Control what data can be inserted:
Example: Set user_id from session
{
"check"
:
{
"user_id"
:
{
"_eq"
:
"X-Hasura-User-Id"
}
}
,
"set"
:
{
"user_id"
:
"X-Hasura-User-Id"
}
}
Example: Validate ownership before insert
{
"check"
:
{
"project"
:
{
"owner_id"
:
{
"_eq"
:
"X-Hasura-User-Id"
}
}
}
}
Update Permissions
Control which rows can be updated and what values can be set:
Example: Update own data only
{
"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
Delete Permissions
Control which rows can be deleted:
Example: Delete own data only
{
"filter"
:
{
"user_id"
:
{
"_eq"
:
"X-Hasura-User-Id"
}
}
}
Authentication Integration
JWT Mode Configuration
Configure Hasura to validate JWT tokens:
Environment Variable:
HASURA_GRAPHQL_JWT_SECRET
=
'{
"type": "RS256",
"key": "-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----"
}'
JWT Claims Structure:
{
"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 Auth0 Integration Auth0 Rule to add Hasura claims: 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: 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 } ) } ) ; Firebase Integration Firebase custom claims: // 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 } } ) ; } Webhook Mode Alternative to JWT - Hasura calls your webhook for each request: Webhook endpoint: 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: HASURA_GRAPHQL_AUTH_HOOK = https://myapp.com/auth-webhook HASURA_GRAPHQL_AUTH_HOOK_MODE = POST Event Triggers Creating Event Triggers Event triggers invoke webhooks on database changes: Via Console: Navigate to Events tab Create Trigger Select table and operations (INSERT, UPDATE, DELETE) Provide webhook URL Configure retry and timeout settings Via Metadata API: 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 } } } Event Payload Structure Webhook receives structured JSON payload: { "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" } } Event Trigger Use Cases Send Welcome Email: // 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: 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: 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 } ) ; } ) ; Actions (Custom Business Logic) Defining Actions Actions extend GraphQL with custom mutations and queries: GraphQL SDL Definition: type Mutation { login ( username : String ! , password : String ! ) : LoginResponse } type LoginResponse { accessToken : String ! refreshToken : String ! user : User ! } Action Configuration: - 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 Action Handler Implementation Express.js Handler: 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 } } ) ; } ) ; Action Permissions Control which roles can execute actions: Via Metadata API: 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: permissions : - role : user - role : admin - role : anonymous 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 Advanced Action Patterns Payment Processing: type Mutation { processPayment ( orderId : ID ! amount : Float ! currency : String ! paymentMethod : String ! ) : PaymentResponse } type PaymentResponse { success : Boolean ! transactionId : String error : String } File Upload: type Mutation { uploadFile ( file : String !

Base64 encoded

fileName : String ! mimeType : String ! ) : FileUploadResponse } type FileUploadResponse { url : String ! fileId : ID ! } Complex Validation: type Mutation { createProject ( name : String ! description : String ! teamMembers : [ ID ! ] ! ) : CreateProjectResponse } type CreateProjectResponse { project : Project errors : [ ValidationError ! ] } type ValidationError { field : String ! message : String ! } Remote Schemas Adding Remote Schemas Integrate external GraphQL APIs: Via Metadata API: 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 } } } Remote Schema Customization Customize type and field names to avoid conflicts: { "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" } ] } } } } Remote Schema Permissions Apply role-based permissions to remote schemas: Original Remote Schema: 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: type User { first_name : String ! last_name : String ! } type Query { get_users_by_name ( first_name : String ! , last_name : String ) : [ User ] } Via Metadata API: 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] }" } } } Remote Schema Argument Presets Automatically inject session variables into remote schema queries: Session Variable Preset: 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: 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): 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: CREATE TABLE customer ( id SERIAL PRIMARY KEY , name TEXT NOT NULL ) ; Remote Schema (Payments API): type Transaction { customer_id : Int ! amount : Int ! time : String ! merchant : String ! } type Query { transactions ( customer_id : String ! , limit : Int ) : [ Transaction ] } Remote Relationship Definition: - 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: query { customer { name customer_transactions_history { amount time } } } Production Deployment Docker Deployment docker-compose.yml: 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 : Kubernetes Deployment hasura-deployment.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 Environment Variables (Production) Essential Production Config:

Database

HASURA_GRAPHQL_DATABASE_URL

postgres://user:password@host:5432/dbname

Security

HASURA_GRAPHQL_ADMIN_SECRET

strong-random-secret HASURA_GRAPHQL_JWT_SECRET = '{"type":"RS256","key":"..."}' HASURA_GRAPHQL_UNAUTHORIZED_ROLE = anonymous

Performance

HASURA_GRAPHQL_ENABLE_CONSOLE

false HASURA_GRAPHQL_DEV_MODE = false HASURA_GRAPHQL_ENABLE_TELEMETRY = false

Logging

HASURA_GRAPHQL_ENABLED_LOG_TYPES

startup,http-log,webhook-log,websocket-log

Rate Limiting

HASURA_GRAPHQL_RATE_LIMIT_PER_MINUTE

1000

CORS

HASURA_GRAPHQL_CORS_DOMAIN

https://myapp.com,https://admin.myapp.com

Connections

HASURA_GRAPHQL_PG_CONNECTIONS

50 HASURA_GRAPHQL_PG_TIMEOUT = 60 Monitoring and Observability Health Check Endpoint: curl http://hasura:8080/healthz

Returns: OK

Prometheus Metrics: HASURA_GRAPHQL_ENABLE_METRICS = true HASURA_GRAPHQL_METRICS_SECRET = metrics-secret

Access at: http://hasura:8080/v1/metrics

Structured Logging: HASURA_GRAPHQL_ENABLED_LOG_TYPES = startup,http-log,webhook-log,websocket-log,query-log HASURA_GRAPHQL_LOG_LEVEL = info APM Integration (Datadog example): 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: 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):

Start console with migration tracking

hasura console

Make changes in console UI

Migrations auto-generated in migrations/ folder

Manual migration:

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): 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): DROP TABLE IF EXISTS public . users CASCADE ; Applying Migrations Apply migrations:

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 Exporting and Importing Metadata Export metadata: hasura metadata export

Exports to metadata/ folder

Apply metadata: hasura metadata apply

Applies metadata from metadata/ folder

Reload metadata:
hasura metadata reload
CI/CD Integration
GitHub Actions example:
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
Always use ADMIN_SECRET in production
Never expose admin API without authentication
Rotate secrets regularly
Use strong, random secrets (min 32 characters)
Implement proper JWT validation
Use RS256 (asymmetric) in production
Set appropriate token expiration
Validate issuer and audience claims
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
Disable console in production
HASURA_GRAPHQL_ENABLE_CONSOLE=false
Use metadata files and CLI for changes
Enable rate limiting
Protect against DoS attacks
Set per-role limits if needed
Monitor and adjust based on usage
Validate webhook payloads
Use webhook secrets for event triggers
Validate action inputs
Sanitize all user inputs
Performance Best Practices
Optimize database queries
Create appropriate indexes
Use database views for complex queries
Leverage PostgreSQL performance tuning
Use query caching
Enable @cached directive for expensive queries
Set appropriate TTL values
Cache at CDN level when possible
Limit query depth and complexity
Set max query depth limits
Restrict deeply nested queries
Use pagination for large result sets
Configure connection pooling
Tune
HASURA_GRAPHQL_PG_CONNECTIONS
Monitor connection usage
Use PgBouncer for large deployments
Optimize subscriptions
Use subscription multiplexing
Limit concurrent subscriptions per client
Consider polling for less time-sensitive data
Development Workflow Best Practices
Version control metadata
Commit metadata/ folder to Git
Use migrations for all schema changes
Review metadata changes in PRs
Environment separation
Development, staging, production environments
Use different admin secrets per environment
Test migrations in staging first
Testing strategy
Test permissions thoroughly
Integration test event triggers
Test action handlers independently
Documentation
Document custom actions and their inputs/outputs
Explain complex permission rules
Maintain API documentation for consumers
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:
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):
{
"filter"
:
{
"organization_id"
:
{
"_eq"
:
"X-Hasura-Org-Id"
}
}
}
JWT Claims:
{
"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:
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):
{
"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:
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
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
type
Mutation
{
processPayment
(
orderId
:
ID
!
paymentMethodId
:
String
!
)
:
PaymentResponse
}
type
PaymentResponse
{
success
:
Boolean
!
orderId
:
ID
!
transactionId
:
String
error
:
String
}
Pattern 4: Real-Time Collaboration
Schema:
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):
{
"filter"
:
{
"_or"
:
[
{
"owner_id"
:
{
"_eq"
:
"X-Hasura-User-Id"
}
}
,
{
"collaborators"
:
{
"user_id"
:
{
"_eq"
:
"X-Hasura-User-Id"
}
}
}
]
}
}
GraphQL Subscription: Real-time updates
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:
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:
-
function
:
name
:
get_user_stats
schema
:
public
configuration
:
custom_root_fields
:
function
:
getUserStats
GraphQL Query:
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
Hasura Docs:
https://hasura.io/docs
Hasura GraphQL API Reference:
https://hasura.io/docs/latest/api-reference
Hasura Cloud:
https://hasura.io/cloud
Learning Resources
Hasura Learn:
https://hasura.io/learn
Hasura Blog:
https://hasura.io/blog
Hasura YouTube:
https://youtube.com/hasurahq
Community
Discord:
https://discord.gg/hasura
GitHub:
https://github.com/hasura/graphql-engine
Forum:
https://github.com/hasura/graphql-engine/discussions
Tools and Integrations
Hasura CLI:
https://hasura.io/docs/latest/hasura-cli/overview
Hasura Cloud Console:
https://cloud.hasura.io
GraphQL Code Generator:
https://www.graphql-code-generator.com
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
返回排行榜