Hasura GraphQL Engine


Hasura is the core GraphQL API engine in nself, automatically generating a complete GraphQL API from your PostgreSQL database schema. This guide covers everything you need to know about using Hasura in your nself deployment.

What is Hasura?

Hasura GraphQL Engine is an open-source engine that connects to your databases and microservices to provide a unified GraphQL API. In nself, Hasura serves as:

  • Auto-generated API: Creates GraphQL queries, mutations, and subscriptions from your database schema
  • Real-time Subscriptions: Live data updates with GraphQL subscriptions
  • Authorization Layer: Fine-grained access control with role-based permissions
  • API Gateway: Unifies multiple data sources under a single GraphQL endpoint

Zero Configuration

With nself, Hasura is pre-configured and ready to use. Simply define your database schema, and your GraphQL API is automatically available.

Accessing Hasura Console

Once your nself environment is running, you can access the Hasura Console:

# Start your nself environment
nself up

# Access Hasura Console at:
# Development: http://localhost:8080/console
# With SSL: https://api.local.nself.org/console

Console Features

  • Data Tab: Browse and modify database tables
  • GraphiQL: Interactive GraphQL playground
  • Remote Schemas: Connect external GraphQL APIs
  • Actions: Custom business logic endpoints
  • Events: Database event triggers
  • Settings: Configuration and permissions

Database Integration

Automatic Schema Detection

Hasura automatically detects your PostgreSQL schema and creates GraphQL types:

-- PostgreSQL table
CREATE TABLE users (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  email VARCHAR(255) UNIQUE NOT NULL,
  name VARCHAR(255),
  created_at TIMESTAMP DEFAULT NOW(),
  updated_at TIMESTAMP DEFAULT NOW()
);

Becomes this GraphQL schema:

type User {
  id: uuid!
  email: String!
  name: String
  created_at: timestamp!
  updated_at: timestamp!
}

type Query {
  users: [User!]!
  users_by_pk(id: uuid!): User
  users_aggregate: UserAggregate!
}

type Mutation {
  insert_users(objects: [UsersInsertInput!]!): UsersMutationResponse
  update_users(where: UsersBoolExp!, _set: UsersSetInput!): UsersMutationResponse
  delete_users(where: UsersBoolExp!): UsersMutationResponse
}

type Subscription {
  users(where: UsersBoolExp): [User!]!
  users_by_pk(id: uuid!): User
}

Relationships

Hasura automatically detects foreign key relationships:

-- Posts table with foreign key to users
CREATE TABLE posts (
  id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  title VARCHAR(255) NOT NULL,
  content TEXT,
  author_id UUID REFERENCES users(id),
  created_at TIMESTAMP DEFAULT NOW()
);

Enables nested GraphQL queries:

query GetPostsWithAuthors {
  posts {
    id
    title
    content
    author {
      name
      email
    }
    created_at
  }
}

query GetUserWithPosts {
  users_by_pk(id: "123e4567-e89b-12d3-a456-426614174000") {
    name
    email
    posts {
      title
      created_at
    }
  }
}

GraphQL Operations

Queries

# Fetch all users
query AllUsers {
  users {
    id
    name
    email
  }
}

# Fetch with filtering
query ActiveUsers {
  users(where: {status: {_eq: "active"}}) {
    id
    name
    email
  }
}

# Fetch with pagination
query UsersPage {
  users(limit: 10, offset: 20, order_by: {created_at: desc}) {
    id
    name
    email
  }
}

Mutations

# Insert single user
mutation CreateUser {
  insert_users_one(object: {
    name: "John Doe"
    email: "john@example.com"
  }) {
    id
    name
    email
  }
}

# Insert multiple users
mutation CreateUsers {
  insert_users(objects: [
    {name: "Alice", email: "alice@example.com"},
    {name: "Bob", email: "bob@example.com"}
  ]) {
    affected_rows
    returning {
      id
      name
    }
  }
}

# Update user
mutation UpdateUser {
  update_users_by_pk(
    pk_columns: {id: "user-id-here"}
    _set: {name: "Jane Doe"}
  ) {
    id
    name
  }
}

# Delete user
mutation DeleteUser {
  delete_users_by_pk(id: "user-id-here") {
    id
    name
  }
}

Subscriptions

# Real-time updates for all users
subscription UsersSubscription {
  users {
    id
    name
    email
    status
  }
}

# Subscribe to specific user changes
subscription UserSubscription {
  users_by_pk(id: "user-id-here") {
    id
    name
    email
    status
  }
}

# Subscribe with filters
subscription OnlineUsers {
  users(where: {status: {_eq: "online"}}) {
    id
    name
    last_seen
  }
}

Authentication & Authorization

JWT Configuration

Hasura uses JWT tokens for authentication. nself pre-configures JWT settings:

# In .env.local
HASURA_GRAPHQL_JWT_SECRET={"type":"HS256","key":"your-jwt-secret-key"}
HASURA_GRAPHQL_ADMIN_SECRET=your-admin-secret

# JWT token structure
{
  "sub": "user-id",
  "iat": 1516239022,
  "exp": 1516325422,
  "https://hasura.io/jwt/claims": {
    "x-hasura-allowed-roles": ["user", "moderator"],
    "x-hasura-default-role": "user",
    "x-hasura-user-id": "user-id"
  }
}

Role-Based Access Control

Define permissions for different user roles:

// User role permissions for 'posts' table
{
  "select": {
    "columns": ["id", "title", "content", "created_at"],
    "filter": {"author_id": {"_eq": "X-Hasura-User-Id"}}
  },
  "insert": {
    "columns": ["title", "content"],
    "set": {"author_id": "X-Hasura-User-Id"}
  },
  "update": {
    "columns": ["title", "content"],
    "filter": {"author_id": {"_eq": "X-Hasura-User-Id"}}
  }
}

Row Level Security

-- PostgreSQL Row Level Security
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;

-- Policy: Users can only see their own posts
CREATE POLICY posts_select_policy ON posts 
  FOR SELECT 
  USING (author_id = current_setting('hasura.user_id')::uuid);

-- Policy: Users can only update their own posts  
CREATE POLICY posts_update_policy ON posts 
  FOR UPDATE 
  USING (author_id = current_setting('hasura.user_id')::uuid);

Custom Business Logic

Actions

Actions let you add custom business logic to your GraphQL schema:

# Define custom action
type Mutation {
  sendPasswordResetEmail(email: String!): SendPasswordResetResponse
}

type SendPasswordResetResponse {
  success: Boolean!
  message: String!
}
// Action handler (in your NestJS service)
@Post('send-password-reset')
async sendPasswordReset(@Body() body: { email: string }) {
  // Custom business logic
  const user = await this.userService.findByEmail(body.email);
  
  if (!user) {
    return { success: false, message: 'User not found' };
  }
  
  await this.emailService.sendPasswordReset(user.email);
  return { success: true, message: 'Reset email sent' };
}

Remote Schemas

Connect external GraphQL APIs:

# Add remote schema via Hasura console
remote_schemas:
  - name: payment_service
    definition:
      url: http://payment-api:3000/graphql
      headers:
        Authorization: "Bearer {{ PAYMENT_API_TOKEN }}"
      timeout_seconds: 30

Event Triggers

Trigger webhooks on database events:

# Event trigger configuration
event_triggers:
  - name: user_created
    definition:
      table:
        name: users
        schema: public
      insert:
        columns: "*"
    webhook: http://webhooks-service:3000/user-created
    headers:
      Authorization: "Bearer {{ WEBHOOK_SECRET }}"
      Content-Type: application/json

Database Functions & Computed Fields

PostgreSQL Functions

-- Create a function to get user's post count
CREATE OR REPLACE FUNCTION user_post_count(user_row users)
RETURNS INTEGER AS $$
BEGIN
  RETURN (
    SELECT COUNT(*)
    FROM posts 
    WHERE author_id = user_row.id
  );
END;
$$ LANGUAGE plpgsql STABLE;

Add as computed field in Hasura:

# Now available in GraphQL
query UsersWithPostCount {
  users {
    id
    name
    email
    post_count  # Computed field
  }
}

Aggregations

query PostStatistics {
  posts_aggregate {
    aggregate {
      count
      avg {
        view_count
      }
      max {
        created_at
      }
    }
  }
}

Performance Optimization

Query Analysis & Optimization

# Enable query analysis in Hasura
HASURA_GRAPHQL_ENABLE_CONSOLE=true
HASURA_GRAPHQL_DEV_MODE=true
HASURA_GRAPHQL_ENABLED_LOG_TYPES=startup,http-log,webhook-log,websocket-log

Database Indexing

-- Create indexes for common query patterns
CREATE INDEX idx_posts_author_id ON posts(author_id);
CREATE INDEX idx_posts_created_at ON posts(created_at DESC);
CREATE INDEX idx_users_email ON users(email);

-- Composite indexes for complex queries
CREATE INDEX idx_posts_author_status ON posts(author_id, status);

Query Complexity Analysis

# Limit query depth and complexity
HASURA_GRAPHQL_QUERY_DEPTH_LIMIT=10
HASURA_GRAPHQL_NODE_LIMIT=100
HASURA_GRAPHQL_RATE_LIMIT_PER_ROLE='{"user": {"max_reqs_per_min": 300}}'

Monitoring & Observability

Logging Configuration

# Enable detailed logging
HASURA_GRAPHQL_ENABLED_LOG_TYPES=startup,http-log,webhook-log,websocket-log
HASURA_GRAPHQL_LOG_LEVEL=info

# Structure logs as JSON
HASURA_GRAPHQL_STRUCTURED_LOGGING=true

Metrics & Health Checks

# Health check endpoint
curl http://localhost:8080/healthz

# Prometheus metrics
curl http://localhost:8080/v1/metrics

# Server configuration
curl http://localhost:8080/v1alpha1/config

Development Workflow

Schema Migrations

# Install Hasura CLI
nself hasura install

# Initialize migrations
nself hasura migrate init

# Create new migration
nself hasura migrate create "add_users_table"

# Apply migrations
nself hasura migrate apply

# Export current schema
nself hasura migrate export

Metadata Management

# Export metadata (permissions, relationships, etc.)
nself hasura metadata export

# Apply metadata
nself hasura metadata apply

# Clear metadata
nself hasura metadata clear

Production Considerations

Security Hardening

# Production security settings
HASURA_GRAPHQL_DEV_MODE=false
HASURA_GRAPHQL_ENABLE_CONSOLE=false
HASURA_GRAPHQL_ADMIN_SECRET=very-secure-admin-secret
HASURA_GRAPHQL_UNAUTHORIZED_ROLE=anonymous

# Enable CORS for specific domains
HASURA_GRAPHQL_CORS_DOMAIN=https://myapp.com,https://api.myapp.com

# Rate limiting
HASURA_GRAPHQL_RATE_LIMIT_PER_ROLE='{"user": {"max_reqs_per_min": 100}}'

High Availability

# Docker Compose scaling
services:
  hasura:
    image: hasura/graphql-engine:latest
    deploy:
      replicas: 3
      resources:
        limits:
          memory: 1G
          cpus: '0.5'
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:8080/healthz"]
      interval: 30s
      timeout: 10s
      retries: 3

Integration with nself Services

NestJS Integration

// GraphQL client in NestJS
@Injectable()
export class GraphQLService {
  private client = new GraphQLClient('http://hasura:8080/v1/graphql', {
    headers: {
      'X-Hasura-Admin-Secret': process.env.HASURA_ADMIN_SECRET
    }
  });

  async createUser(userData: CreateUserInput) {
    const mutation = gql`
      mutation CreateUser($data: users_insert_input!) {
        insert_users_one(object: $data) {
          id
          email
          name
        }
      }
    `;
    
    return this.client.request(mutation, { data: userData });
  }
}

Python Integration

# GraphQL client in FastAPI
from gql import gql, Client
from gql.transport.aiohttp import AIOHTTPTransport

class GraphQLService:
    def __init__(self):
        transport = AIOHTTPTransport(
            url="http://hasura:8080/v1/graphql",
            headers={"X-Hasura-Admin-Secret": os.getenv("HASURA_ADMIN_SECRET")}
        )
        self.client = Client(transport=transport)
    
    async def get_users(self):
        query = gql("""
            query GetUsers {
                users {
                    id
                    name
                    email
                }
            }
        """)
        return await self.client.execute_async(query)

Troubleshooting

Common Issues

# Check Hasura logs
nself logs hasura

# Verify database connection
nself hasura console --database-url postgresql://user:pass@db:5432/dbname

# Test GraphQL endpoint
curl -H "Content-Type: application/json" \
     -H "X-Hasura-Admin-Secret: yoursecret" \
     -d '{"query": "{ __schema { types { name } } }"}' \
     http://localhost:8080/v1/graphql

Performance Debugging

# Enable query analysis
HASURA_GRAPHQL_ENABLE_ALLOWLIST=false
HASURA_GRAPHQL_DEV_MODE=true

# Check slow queries in PostgreSQL
SELECT query, mean_time, calls 
FROM pg_stat_statements 
ORDER BY mean_time DESC 
LIMIT 10;

Next Steps

Now that you understand Hasura in nself:

Hasura provides a powerful, production-ready GraphQL API with minimal configuration. Use the console to explore your API and build sophisticated applications quickly.