A pre-NDC request plugin that routes mutations to your primary database and distributes queries across read replicas using round-robin load balancing.
graph LR
Client[Client] --> DDN[Hasura DDN Engine]
DDN --> Plugin[Dynamic Connection Plugin]
Plugin --> Connector[Data Connector]
Connector --> Primary[(Primary DB)]
Connector --> Replica1[(Replica 1)]
Connector --> Replica2[(Replica 2)]
Plugin -.->|mutation| Primary
Plugin -.->|query round-robin| Replica1
Plugin -.->|query round-robin| Replica2
Plugin -.->|read-no-stale| Primary
The plugin sits between Hasura DDN and your data connector, inspecting each request and injecting the appropriate connection_name based on the operation type.
docker run -p 8787:8787 \
-e APP_PRE_NDC_WEBHOOK_TOKEN=your-secret-token \
ghcr.io/hasura/engine-plugin-dynamic-connection:latestOr from source:
npm install && npm startCreate app/metadata/dynamic-routing.hml:
kind: LifecyclePluginHook
version: v1
definition:
pre: ndcRequest
name: dynamic_routing
url:
valueFromEnv: APP_PRE_NDC_WEBHOOK_URL
connectors:
- postgres # Replace with your connector name
config:
request:
headers:
"hasura-m-auth":
valueFromEnv: APP_PRE_NDC_WEBHOOK_TOKEN
"hasura-replica-connection-names":
valueFromEnv: APP_REPLICA_CONNECTION_NAMES
"hasura-primary-connection-name":
valueFromEnv: APP_PRIMARY_CONNECTION_NAME
session: {}
ndcRequest: {}| Variable | Description | Example |
|---|---|---|
APP_PRE_NDC_WEBHOOK_URL |
URL where the plugin is hosted | http://localhost:8787/pre/ndc |
APP_PRE_NDC_WEBHOOK_TOKEN |
Authentication token for the plugin | zZkhKqFjqXR4g5MZCsJUZCnhCcoPyZ |
APP_REPLICA_CONNECTION_NAMES |
Comma-separated list of read replica connection names | replica1,replica2,replica3 |
APP_PRIMARY_CONNECTION_NAME |
Name of the primary database connection | primary |
| Variable | Description | Default |
|---|---|---|
PORT |
Port on which the plugin server runs | 8787 |
HOST |
Host address to bind to | 0.0.0.0 |
LOG_LEVEL |
Logging level (ERROR, WARN, INFO, DEBUG) |
INFO |
NODE_ENV |
Node environment | development |
Configure multiple connections in your data connector:
# In your connector's configuration.json
{
"connection": {
"primary": {
"uri": "postgresql://user:pass@primary-db:5432/mydb"
},
"replica1": {
"uri": "postgresql://user:pass@replica1-db:5432/mydb"
},
"replica2": {
"uri": "postgresql://user:pass@replica2-db:5432/mydb"
}
}
}# Queries → replicas (round-robin)
query { authors { id name } }
# Mutations → primary
mutation { insert_authors(objects: {name: "John"}) { id } }
# Read-after-write → primary (set header: x-hasura-query-read-no-stale: 1)
query { authors { id name } }GET /health→{"status": "healthy"}GET /ping→{"status": "ok"}
Logging: Set LOG_LEVEL to ERROR, WARN, INFO (default), or DEBUG.
OpenTelemetry: Tracing is supported. Configure endpoint in src/config.js if needed.
Configure PostgreSQL replication (or your database's equivalent):
-- On primary database
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'password';
-- Configure postgresql.conf
wal_level = replica
max_wal_senders = 3On replica servers, configure streaming replication to follow the primary.
Add multiple connections to your connector's configuration.json:
{
"connections": {
"primary": {
"uri": { "variable": "PRIMARY_DATABASE_URL" }
},
"replica1": {
"uri": { "variable": "REPLICA1_DATABASE_URL" }
},
"replica2": {
"uri": { "variable": "REPLICA2_DATABASE_URL" }
}
}
}docker run -d -p 8787:8787 \
-e APP_PRE_NDC_WEBHOOK_TOKEN=$(openssl rand -base64 32) \
ghcr.io/hasura/engine-plugin-dynamic-connection:latestAdd the plugin hook to your metadata (see Quick Start section above).
Set environment variables in your .env:
APP_PRE_NDC_WEBHOOK_URL=http://plugin:8787/pre/ndc
APP_PRE_NDC_WEBHOOK_TOKEN=<same-token-from-step-3>
APP_PRIMARY_CONNECTION_NAME=primary
APP_REPLICA_CONNECTION_NAMES=replica1,replica2ddn supergraph build local
ddn run docker-start
# Test query (should hit replica)
curl -X POST http://localhost:3000/graphql \
-d '{"query": "{ authors { id } }"}'
# Test mutation (should hit primary)
curl -X POST http://localhost:3000/graphql \
-d '{"query": "mutation { insert_authors(objects: {name: \"test\"}) { id } }"}'
# Check plugin logs
docker logs <plugin-container-id>npm install
npm run dev # Start with hot reload
npm run lint # Check code styleDocker:
docker build -t your-registry/dynamic-connection-plugin .
docker push your-registry/dynamic-connection-pluginKubernetes: Use health checks on /health endpoint with liveness/readiness probes.
- Round-robin state is in-memory (resets on restart)
- For multi-instance deployments, consider using Redis for shared state
- Use strong tokens and HTTPS in production
- Connection names must match your data connector configuration
401/400 Unauthorized: Check APP_PRE_NDC_WEBHOOK_TOKEN matches in both plugin and Hasura DDN config.
Queries not distributed: Verify APP_REPLICA_CONNECTION_NAMES is set correctly and check plugin logs.
Connection not found: Ensure connection names match exactly between plugin config and data connector.
MIT License