Subscription Context with Convertors
Convertor Subscription Pattern
Subscription context flows through SubscriptionRequestParams and Ferryâs updateCacheHandlerContext.
Subscription Context with Convertors
Section titled âSubscription Context with ConvertorsâThe Problem
Section titled âThe ProblemâSubscriptions often need context for filtering:
- Chat message subscriptions need current chat ID and user ID
- Notification subscriptions need the current event/conference ID
- Presence subscriptions need the current room or channel
Defining Subscription Context
Section titled âDefining Subscription ContextâCreate a serializable context class:
class ChatMessageSubscriptionContext { final String chatId; final String currentUserId;
const ChatMessageSubscriptionContext({ required this.chatId, required this.currentUserId, });
Map<String, dynamic> toJson() => { 'chatId': chatId, 'currentUserId': currentUserId, };
factory ChatMessageSubscriptionContext.fromJson(Map<String, dynamic> json) { return ChatMessageSubscriptionContext( chatId: json['chatId'] as String, currentUserId: json['currentUserId'] as String, ); }}Subscription Request Convertor
Section titled âSubscription Request Convertorâ@riverpodConvertor<GSubscribeToChatMessagesReq, SubscriptionRequestParams<ChatMessageSubscriptionContext>>chatMessageSubscriptionConvertor(Ref ref) { return Convertor((params) { return GSubscribeToChatMessagesReq((builder) { builder ..requestId = 'chat_message_subscription_${params.context?.chatId}' ..updateCacheHandlerKey = 'messageSubscriptionCacheHandler' ..updateCacheHandlerContext = params.context?.toJson(); }); });}Subscription Datasource Pipeline
Section titled âSubscription Datasource Pipelineâ@riverpodStreamConvertor<GSubscribeToChatMessagesData_messageSubscription, SubscriptionRequestParams<ChatMessageSubscriptionContext>>chatMessageSubscriptionDatasource(Ref ref) { return GraphQLRequestExecutor<GSubscribeToChatMessagesData, SubscriptionRequestParams<ChatMessageSubscriptionContext>, GSubscribeToChatMessagesVars>( gqlClient: ref.watch(gqlClientProvider), convertor: ref.watch(chatMessageSubscriptionConvertorProvider), ) .then(GraphQLStreamConvertor()) .map((data) => data.messageSubscription);}Repository Usage
Section titled âRepository UsageâStream<ChatMessageModel> subscribeToMessages({ required String chatId, required String currentUserId,}) { final context = ChatMessageSubscriptionContext( chatId: chatId, currentUserId: currentUserId, ); final params = SubscriptionRequestParams(context: context);
return subscriptionDatasource .thenMap(messageModelConvertor) .execute(params);}Cache Handler with Subscription Context
Section titled âCache Handler with Subscription ContextâThe CacheHandlerSpecs can access subscription context for intelligent cache updates:
CacheHandlerSpecs.merge( cacheHandlerKey: 'messageSubscriptionCacheHandler', mapToCachedRequest: Convertor((subscriptionRequest) { // Use the subscription context to map to the correct list query final context = subscriptionRequest.updateCacheHandlerContext; if (context == null) return null; final subContext = ChatMessageSubscriptionContext.fromJson(context);
return GQueryChatMessagesReq((b) => b ..requestId = 'chat_messages_${subContext.chatId}' ..vars.G_filter = GmessageFilters((f) => f ..systemParentId = subContext.chatId).toBuilder()); }), mapResponse: Convertor((data) => data.messageSubscription), mergeCachedData: Convertor(((oldData, newMessage)) { final items = oldData.message!.messageItems!.toList(); items.insert(0, newMessage); // Prepend new message return oldData.rebuild((b) => b..message.messageItems.replace(items)); }),);Key Points
Section titled âKey Pointsâ- Subscription contexts must be JSON-serializable (Ferry passes them as
Map<String, dynamic>) - Use unique
requestIdper subscription instance (include context identifiers) updateCacheHandlerKeylinks the subscription to itsCacheHandlerSpecs- Context filtering happens in the
CacheHandlerSpecs, not in the subscription stream