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.
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:
With nself, Hasura is pre-configured and ready to use. Simply define your database schema, and your GraphQL API is automatically available.
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
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
}
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
}
}
}
# 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
}
}
# 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
}
}
# 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
}
}
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"
}
}
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"}}
}
}
-- 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);
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' };
}
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
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
-- 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
}
}
query PostStatistics {
posts_aggregate {
aggregate {
count
avg {
view_count
}
max {
created_at
}
}
}
}
# 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
-- 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);
# 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}}'
# 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
# Health check endpoint
curl http://localhost:8080/healthz
# Prometheus metrics
curl http://localhost:8080/v1/metrics
# Server configuration
curl http://localhost:8080/v1alpha1/config
# 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
# Export metadata (permissions, relationships, etc.)
nself hasura metadata export
# Apply metadata
nself hasura metadata apply
# Clear metadata
nself hasura metadata clear
# 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}}'
# 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
// 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 });
}
}
# 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)
# 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
# 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;
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.