GraphQL Subscriptions
FLXBL provides real-time updates through GraphQL subscriptions. Subscribe to entity lifecycle events (create, update, delete) and receive instant notifications when data changes.
Overview
For each entity in your schema, FLXBL automatically generates three subscription types:
| Subscription | Event | Returns |
|---|---|---|
{entity}Created | New entity created | Full entity object |
{entity}Updated | Entity modified | Full entity object with updated fields |
{entity}Deleted | Entity removed | Deleted entity ID |
Basic Usage
Subscribe to entity lifecycle events using standard GraphQL subscription syntax:
# Subscribe to new product creations
subscription {
productCreated {
id
name
price
createdAt
}
}
# Subscribe to updates on a specific product
subscription {
productUpdated(id: "node_abc123") {
id
name
price
updatedAt
}
}
# Subscribe to all product deletions
subscription {
productDeleted
}Filtering Events
Filter events using the where argument for created events,
or the id argument to watch a specific entity:
# Subscribe to products with a filter
subscription {
productCreated(where: { price: { gte: 100 } }) {
id
name
price
}
}
# Subscribe to updates on any product (no filter)
subscription {
productUpdated {
id
name
price
updatedAt
}
}Subscription Types
The generated GraphQL schema includes subscription types for all your entities:
# Available subscription types for each entity
type Subscription {
# Fires when a new entity is created
productCreated(where: ProductWhereInput): Product!
# Fires when an entity is updated (optional ID filter)
productUpdated(id: ID): Product!
# Fires when an entity is deleted (returns the deleted ID)
productDeleted(id: ID): ID!
# Same pattern for all entities:
# customerCreated, customerUpdated, customerDeleted
# orderCreated, orderUpdated, orderDeleted
# etc.
}Event Payload
Subscription events return data in standard GraphQL response format:
# Event payload format
{
"data": {
"productCreated": {
"id": "node_xyz789",
"name": "New Widget",
"price": 49.99,
"createdAt": "2025-01-15T10:30:00Z"
}
}
}
# Delete event payload (returns only the ID)
{
"data": {
"productDeleted": "node_abc123"
}
}WebSocket Connection
Subscriptions use the graphql-ws protocol over WebSockets. Connect to the subscription endpoint and authenticate with your API key:
# WebSocket endpoint
wss://api.flxbl.dev/api/v1/dynamic-gql/ws
# Connection init payload (tenantId is extracted from JWT token)
{
"type": "connection_init",
"payload": {
"Authorization": "Bearer flxbl_your_api_key"
}
}
# Subscribe message
{
"id": "1",
"type": "subscribe",
"payload": {
"query": "subscription { productCreated { id name } }"
}
}Connection Protocol
- Open WebSocket connection to
wss://api.flxbl.dev/api/v1/dynamic-gql/ws - Send
connection_initmessage with authentication - Receive
connection_ackon successful auth - Send
subscribemessages for each subscription - Receive
nextmessages with event data - Send
completeto unsubscribe
Tenant Isolation
All subscription events are scoped to your tenant. You will only receive events for entities within your tenant's data, even when subscribing to all events without filters.
Security: Events are published to tenant-specific channels. There is no way to subscribe to events from other tenants.
Use Cases
- Real-time dashboards: Update metrics and charts as data changes
- Live feeds: Display new content immediately as it's created
- Collaborative editing: Sync changes across multiple users
- Notifications: Trigger alerts when specific events occur
- Cache invalidation: Automatically refresh cached data when it changes
Best Practices
- Use specific filters to reduce unnecessary event traffic
- Handle connection drops and implement reconnection logic
- Unsubscribe when components unmount to prevent memory leaks
- Consider using the
idfilter for update/delete subscriptions when watching specific entities
Client Implementation
Ready-to-use client examples for integrating FLXBL subscriptions into your application. All examples use the graphql-ws protocol.
JavaScript / TypeScript
Install the graphql-ws package:
npm install graphql-ws import { createClient } from 'graphql-ws';
// Create a WebSocket client with authentication
const client = createClient({
url: 'wss://api.flxbl.dev/api/v1/dynamic-gql/ws',
connectionParams: {
Authorization: `Bearer ${API_KEY}`,
},
// Automatic reconnection with exponential backoff
retryAttempts: 5,
shouldRetry: () => true,
on: {
connected: () => console.log('Connected to FLXBL'),
closed: () => console.log('Connection closed'),
error: (err) => console.error('Connection error:', err),
},
});
// Subscribe to product creations
const unsubscribe = client.subscribe(
{
query: `subscription {
productCreated {
id
name
price
createdAt
}
}`,
},
{
next: (data) => {
console.log('New product:', data.data.productCreated);
},
error: (err) => {
console.error('Subscription error:', err);
},
complete: () => {
console.log('Subscription completed');
},
}
);
// Unsubscribe when done
// unsubscribe();React Hook
A reusable React hook for managing subscriptions with automatic cleanup:
import { useEffect, useState, useCallback } from 'react';
import { createClient, Client } from 'graphql-ws';
// Custom hook for FLXBL subscriptions
function useSubscription<T>(query: string, variables?: Record<string, unknown>) {
const [data, setData] = useState<T | null>(null);
const [error, setError] = useState<Error | null>(null);
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
const client = createClient({
url: 'wss://api.flxbl.dev/api/v1/dynamic-gql/ws',
connectionParams: {
Authorization: `Bearer ${process.env.REACT_APP_FLXBL_API_KEY}`,
},
on: {
connected: () => setIsConnected(true),
closed: () => setIsConnected(false),
},
});
const unsubscribe = client.subscribe(
{ query, variables },
{
next: (result) => setData(result.data as T),
error: (err) => setError(err as Error),
complete: () => console.log('Subscription complete'),
}
);
return () => {
unsubscribe();
client.dispose();
};
}, [query, JSON.stringify(variables)]);
return { data, error, isConnected };
}
// Usage in a component
function ProductFeed() {
const { data, error, isConnected } = useSubscription<{
productCreated: { id: string; name: string; price: number };
}>(`
subscription {
productCreated {
id
name
price
}
}
`);
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<span>Status: {isConnected ? '🟢 Connected' : '🔴 Disconnected'}</span>
{data && (
<div>
<h3>New Product!</h3>
<p>{data.productCreated.name} - ${data.productCreated.price}</p>
</div>
)}
</div>
);
}Python
Install the websockets package:
pip install websockets import asyncio
import json
from websockets import connect
API_KEY = "flxbl_your_api_key"
WS_URL = "wss://api.flxbl.dev/api/v1/dynamic-gql/ws"
async def subscribe_to_products():
async with connect(WS_URL) as websocket:
# Step 1: Send connection_init with auth
await websocket.send(json.dumps({
"type": "connection_init",
"payload": {
"Authorization": f"Bearer {API_KEY}"
}
}))
# Step 2: Wait for connection_ack
response = await websocket.recv()
ack = json.loads(response)
if ack["type"] != "connection_ack":
raise Exception(f"Connection failed: {ack}")
print("✓ Connected to FLXBL")
# Step 3: Subscribe to product creations
await websocket.send(json.dumps({
"id": "product-sub-1",
"type": "subscribe",
"payload": {
"query": """
subscription {
productCreated {
id
name
price
createdAt
}
}
"""
}
}))
print("✓ Subscribed to productCreated events")
# Step 4: Listen for events
try:
async for message in websocket:
data = json.loads(message)
if data["type"] == "next":
product = data["payload"]["data"]["productCreated"]
print(f"New product: {product['name']} (${product['price']})")
elif data["type"] == "error":
print(f"Error: {data['payload']}")
break
elif data["type"] == "complete":
print("Subscription completed")
break
except KeyboardInterrupt:
# Step 5: Unsubscribe gracefully
await websocket.send(json.dumps({
"id": "product-sub-1",
"type": "complete"
}))
print("Unsubscribed")
# Run the subscription
asyncio.run(subscribe_to_products())Error Handling & Reconnection
For production applications, implement robust reconnection logic to handle network interruptions gracefully:
import { createClient } from 'graphql-ws';
// Production-ready client with robust reconnection
const client = createClient({
url: 'wss://api.flxbl.dev/api/v1/dynamic-gql/ws',
connectionParams: async () => ({
// Fetch fresh token on each reconnection
Authorization: `Bearer ${await getApiKey()}`,
}),
// Retry configuration
retryAttempts: Infinity, // Keep trying forever
retryWait: async (retries) => {
// Exponential backoff: 1s, 2s, 4s, 8s, max 30s
const delay = Math.min(1000 * Math.pow(2, retries), 30000);
await new Promise(resolve => setTimeout(resolve, delay));
},
// Only retry on connection errors, not subscription errors
shouldRetry: (errOrCloseEvent) => {
// Don't retry on auth errors (4401)
if ('code' in errOrCloseEvent && errOrCloseEvent.code === 4401) {
console.error('Authentication failed - check your API key');
return false;
}
return true;
},
// Connection lifecycle hooks
on: {
connecting: () => console.log('Connecting...'),
connected: (socket) => console.log('Connected!'),
closed: (event) => console.log('Disconnected:', event.reason),
error: (error) => console.error('Error:', error),
},
// Keep-alive ping every 30 seconds
keepAlive: 30000,
});Common Error Codes
| Code | Meaning | Action |
|---|---|---|
4401 | Authentication failed | Check API key, don't retry |
4400 | Invalid subscription query | Fix query syntax, don't retry |
1006 | Abnormal closure (network) | Retry with backoff |
1001 | Server going away | Retry immediately |
Next Steps
- API Reference - REST and GraphQL API documentation
- Query DSL - Learn the query language
- Webhooks - Server-side event notifications