Building Authenticated GraphQL Subscriptions with Hasura and Flutter
Aug 6, 2025



Summary
Summary
Summary
Summary
This tutorial covers building authenticated GraphQL subscriptions with Hasura and Flutter. Learn to configure JWT in Hasura, integrate `graphql_flutter` with HTTP and WebSocket links, authenticate subscription connections, manage real-time event streams, handle errors, and clean up resources for secure, reactive mobile development.
This tutorial covers building authenticated GraphQL subscriptions with Hasura and Flutter. Learn to configure JWT in Hasura, integrate `graphql_flutter` with HTTP and WebSocket links, authenticate subscription connections, manage real-time event streams, handle errors, and clean up resources for secure, reactive mobile development.
This tutorial covers building authenticated GraphQL subscriptions with Hasura and Flutter. Learn to configure JWT in Hasura, integrate `graphql_flutter` with HTTP and WebSocket links, authenticate subscription connections, manage real-time event streams, handle errors, and clean up resources for secure, reactive mobile development.
This tutorial covers building authenticated GraphQL subscriptions with Hasura and Flutter. Learn to configure JWT in Hasura, integrate `graphql_flutter` with HTTP and WebSocket links, authenticate subscription connections, manage real-time event streams, handle errors, and clean up resources for secure, reactive mobile development.
Key insights:
Key insights:
Key insights:
Key insights:
Setting up Hasura with JWT Authentication: Configure Hasura's JWT secret and permissions to control subscription access.
Integrating GraphQL Client in Flutter: Use
graphql_flutter
withHttpLink
andAuthLink
to include JWT in requests.Authenticating WebSocket Connections: Initialize
WebSocketLink
with the JWT ininitialPayload
to secure live data streams.Managing Subscription Events: Employ
client.subscribe
andStreamBuilder
to receive and render real-time updates.Error Handling and Cleanup: Implement retry logic, token refresh, and cancel subscriptions to maintain a robust connection.
Introduction
Building real-time features in mobile apps often requires a robust subscription mechanism. With Hasura's GraphQL engine and Flutter's flexible ecosystem, you can stream live updates securely using authenticated GraphQL subscriptions. This tutorial walks through setting up JWT authentication in Hasura, configuring a Flutter GraphQL client, and managing secure WebSocket subscriptions to deliver real-time data in your mobile development projects.
Setting up Hasura with JWT Authentication
First, configure Hasura to accept JWT tokens for authorization. In your Hasura console under Settings → JWT, provide your JWT issuer and JWK URL or public key. For example, if using Auth0:
Set HASURA_GRAPHQL_JWT_SECRET
to:
{
"type": "RS256",
"key_url": "https://your-domain/.well-known/jwks.json"
}
Next, define role-based permissions on tables. Under Data → your table → Permissions, grant select
or insert
permissions to the user
role, leveraging X-Hasura-User-Id
from the JWT payload. This ensures only authenticated users receive subscription updates.
Integrating GraphQL Client in Flutter
Add the graphql_flutter
package to your pubspec.yaml
:
dependencies:
flutter:
sdk: flutter
graphql_flutter
Initialize the client in your main app file. Use an HttpLink
combined with an AuthLink
to include the JWT on every request.
final authLink = AuthLink(
getToken: () async => 'Bearer ${await secureStorage.read(key: 'jwt')}',
);
final httpLink = HttpLink('https://hasura.your-domain/v1/graphql');
final link = authLink.concat(httpLink);
final client = GraphQLClient(
link: link,
cache: GraphQLCache(store: InMemoryStore()),
);
Wrap your app with GraphQLProvider
and pass this client to enable queries, mutations, and subscriptions throughout your widget tree.
Authenticating WebSocket Connections
Subscriptions in GraphQL require a WebSocket link. You must forward the same JWT during the WebSocket handshake. Configure WebSocketLink
accordingly:
final wsLink = WebSocketLink(
'wss://hasura.your-domain/v1/graphql',
config: SocketClientConfig(
initialPayload: () async => {
'headers': {'Authorization': 'Bearer ${await secureStorage.read(key: 'jwt')}'},
},
),
);
final combinedLink = Link.split(
(request) => request.isSubscription,
wsLink,
link, // from HttpLink + AuthLink
);
This setup ensures that each subscription connection is authenticated using the same JWT as HTTP operations.
Managing Subscription Events
With the client and link configured, you can listen to live updates. Use client.subscribe
to start streaming:
const chatSubscription = gql(r'''
subscription OnNewMessage($roomId: Int!) {
messages(where: {room_id: {_eq: $roomId}}) {
id
content
created_at
}
}
''');
final stream = client.subscribe(
SubscriptionOptions(document: chatSubscription, variables: {'roomId': 42}),
);
stream.listen((result) {
if (result.hasException) {
// Handle errors
return;
}
final messages = result.data?['messages'] as List;
// Update UI with new messages
});
Inside your Flutter widgets, you can use StreamBuilder
to rebuild UI as new data arrives. Ensure you dispose of subscriptions when the widget unmounts to avoid memory leaks.
Error Handling and Cleanup
Network interruptions or authorization failures can occur. Listen for errors on the WebSocket client and implement retry logic. For instance, wrap your stream listener in a try-catch and show a loading or reconnect indicator:
On
AuthError
(e.g., token expired), trigger a token refresh and restart the subscription.On
NetworkError
, implement an exponential backoff reconnection strategy.
Always call stream.drain()
or cancel the subscription in your State.dispose()
method to clean up resources.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Secure, real-time data in Flutter is achievable by combining Hasura's GraphQL subscriptions with JWT authentication and Flutter's graphql_flutter
package. By configuring JWT-based permissions, setting up authenticated WebSocket links, and handling subscription streams responsibly, you can build responsive, secure mobile applications that react instantly to server-side events.
Introduction
Building real-time features in mobile apps often requires a robust subscription mechanism. With Hasura's GraphQL engine and Flutter's flexible ecosystem, you can stream live updates securely using authenticated GraphQL subscriptions. This tutorial walks through setting up JWT authentication in Hasura, configuring a Flutter GraphQL client, and managing secure WebSocket subscriptions to deliver real-time data in your mobile development projects.
Setting up Hasura with JWT Authentication
First, configure Hasura to accept JWT tokens for authorization. In your Hasura console under Settings → JWT, provide your JWT issuer and JWK URL or public key. For example, if using Auth0:
Set HASURA_GRAPHQL_JWT_SECRET
to:
{
"type": "RS256",
"key_url": "https://your-domain/.well-known/jwks.json"
}
Next, define role-based permissions on tables. Under Data → your table → Permissions, grant select
or insert
permissions to the user
role, leveraging X-Hasura-User-Id
from the JWT payload. This ensures only authenticated users receive subscription updates.
Integrating GraphQL Client in Flutter
Add the graphql_flutter
package to your pubspec.yaml
:
dependencies:
flutter:
sdk: flutter
graphql_flutter
Initialize the client in your main app file. Use an HttpLink
combined with an AuthLink
to include the JWT on every request.
final authLink = AuthLink(
getToken: () async => 'Bearer ${await secureStorage.read(key: 'jwt')}',
);
final httpLink = HttpLink('https://hasura.your-domain/v1/graphql');
final link = authLink.concat(httpLink);
final client = GraphQLClient(
link: link,
cache: GraphQLCache(store: InMemoryStore()),
);
Wrap your app with GraphQLProvider
and pass this client to enable queries, mutations, and subscriptions throughout your widget tree.
Authenticating WebSocket Connections
Subscriptions in GraphQL require a WebSocket link. You must forward the same JWT during the WebSocket handshake. Configure WebSocketLink
accordingly:
final wsLink = WebSocketLink(
'wss://hasura.your-domain/v1/graphql',
config: SocketClientConfig(
initialPayload: () async => {
'headers': {'Authorization': 'Bearer ${await secureStorage.read(key: 'jwt')}'},
},
),
);
final combinedLink = Link.split(
(request) => request.isSubscription,
wsLink,
link, // from HttpLink + AuthLink
);
This setup ensures that each subscription connection is authenticated using the same JWT as HTTP operations.
Managing Subscription Events
With the client and link configured, you can listen to live updates. Use client.subscribe
to start streaming:
const chatSubscription = gql(r'''
subscription OnNewMessage($roomId: Int!) {
messages(where: {room_id: {_eq: $roomId}}) {
id
content
created_at
}
}
''');
final stream = client.subscribe(
SubscriptionOptions(document: chatSubscription, variables: {'roomId': 42}),
);
stream.listen((result) {
if (result.hasException) {
// Handle errors
return;
}
final messages = result.data?['messages'] as List;
// Update UI with new messages
});
Inside your Flutter widgets, you can use StreamBuilder
to rebuild UI as new data arrives. Ensure you dispose of subscriptions when the widget unmounts to avoid memory leaks.
Error Handling and Cleanup
Network interruptions or authorization failures can occur. Listen for errors on the WebSocket client and implement retry logic. For instance, wrap your stream listener in a try-catch and show a loading or reconnect indicator:
On
AuthError
(e.g., token expired), trigger a token refresh and restart the subscription.On
NetworkError
, implement an exponential backoff reconnection strategy.
Always call stream.drain()
or cancel the subscription in your State.dispose()
method to clean up resources.
Vibe Studio

Vibe Studio, powered by Steve’s advanced AI agents, is a revolutionary no-code, conversational platform that empowers users to quickly and efficiently create full-stack Flutter applications integrated seamlessly with Firebase backend services. Ideal for solo founders, startups, and agile engineering teams, Vibe Studio allows users to visually manage and deploy Flutter apps, greatly accelerating the development process. The intuitive conversational interface simplifies complex development tasks, making app creation accessible even for non-coders.
Conclusion
Secure, real-time data in Flutter is achievable by combining Hasura's GraphQL subscriptions with JWT authentication and Flutter's graphql_flutter
package. By configuring JWT-based permissions, setting up authenticated WebSocket links, and handling subscription streams responsibly, you can build responsive, secure mobile applications that react instantly to server-side events.
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Build Flutter Apps Faster with Vibe Studio
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.
Vibe Studio is your AI-powered Flutter development companion. Skip boilerplate, build in real-time, and deploy without hassle. Start creating apps at lightning speed with zero setup.











