Docs / Programming & Development / GraphQL API with Hasura

GraphQL API with Hasura

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 239 views · 3 min read

Hasura provides an instant GraphQL API over your PostgreSQL database without writing backend code. It generates queries, mutations, subscriptions, and handles authorization — all configured through a web console. This guide covers deploying Hasura on a VPS for production use.

Installation with Docker

# docker-compose.yml
services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_PASSWORD: postgrespassword
    volumes:
      - pgdata:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready"]
      interval: 5s
      timeout: 5s
      retries: 5

  hasura:
    image: hasura/graphql-engine:v2.40.0
    ports:
      - "8080:8080"
    depends_on:
      postgres:
        condition: service_healthy
    environment:
      HASURA_GRAPHQL_METADATA_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
      HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:postgrespassword@postgres:5432/postgres
      HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
      HASURA_GRAPHQL_ADMIN_SECRET: your-admin-secret-key
      HASURA_GRAPHQL_JWT_SECRET: |
        {"type":"HS256","key":"your-jwt-secret-minimum-32-chars-long"}
      HASURA_GRAPHQL_UNAUTHORIZED_ROLE: anonymous
      HASURA_GRAPHQL_CORS_DOMAIN: "https://example.com,https://app.example.com"
    restart: unless-stopped

volumes:
  pgdata:
# Start Hasura
docker compose up -d

# Access console at http://your-vps:8080/console
# Use admin secret to authenticate

Tracking Tables

-- Create tables in PostgreSQL
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(255) UNIQUE NOT NULL,
    role VARCHAR(20) DEFAULT 'user',
    created_at TIMESTAMP DEFAULT NOW()
);

CREATE TABLE posts (
    id SERIAL PRIMARY KEY,
    title VARCHAR(255) NOT NULL,
    content TEXT,
    author_id INTEGER REFERENCES users(id),
    published BOOLEAN DEFAULT false,
    created_at TIMESTAMP DEFAULT NOW()
);

-- In Hasura Console: Data tab > Track All tables
-- Hasura auto-detects relationships from foreign keys

Queries and Mutations

# Auto-generated GraphQL queries:
query GetUsers {
  users(order_by: { created_at: desc }, limit: 20) {
    id
    name
    email
    posts {
      id
      title
      published
    }
  }
}

mutation CreateUser {
  insert_users_one(object: { name: "Alice", email: "alice@example.com" }) {
    id
    name
  }
}

# Real-time subscription
subscription NewPosts {
  posts(where: { published: { _eq: true } }, order_by: { created_at: desc }) {
    id
    title
    author { name }
  }
}

Permission Rules

# Hasura Console > Permissions tab
# Row-level security based on JWT claims

# User role - can only see own data:
# Select permission on users table:
# Row filter: {"id": {"_eq": "X-Hasura-User-Id"}}
# Column access: id, name, email (not role)

# User role - can only see published posts:
# Select permission on posts table:
# Row filter: {"_or": [{"published": {"_eq": true}}, {"author_id": {"_eq": "X-Hasura-User-Id"}}]}

Remote Schemas and Actions

# Add custom business logic via Actions
# Hasura Console > Actions > Create

# Action definition
type Mutation {
  sendEmail(to: String!, subject: String!, body: String!): EmailResponse
}

type EmailResponse {
  success: Boolean!
  messageId: String
}

# Handler URL: https://your-api.example.com/api/send-email
# Hasura forwards the mutation to your custom endpoint

Nginx Reverse Proxy

server {
    listen 443 ssl http2;
    server_name graphql.example.com;

    ssl_certificate /etc/letsencrypt/live/graphql.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/graphql.example.com/privkey.pem;

    location / {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Metadata Management

# Export metadata for version control
hasura metadata export

# Apply metadata
hasura metadata apply

# Structure:
# metadata/
#   databases/
#   actions.yaml
#   remote_schemas.yaml
#   tables.yaml

Summary

Hasura eliminates the need to write CRUD API code by generating a complete GraphQL API directly from your PostgreSQL schema. Real-time subscriptions, row-level permissions, and remote schema stitching cover most API needs. For custom business logic, Actions forward specific mutations to your own endpoints. Self-hosting on a VPS gives you unlimited API requests, full data control, and the ability to run Hasura alongside your existing database.

Was this article helpful?