Skip to content

Repository Context with Convertors

Convertor Context Passing

Context data flows naturally through Convertor input parameters and subscription context.

In the Convertor pattern, context data (user IDs, chat IDs, filters) flows naturally through typed input parameters. There is no separate “RequestContext” object - the Convertor’s input parameter carries all needed context.

Stream<List<NotificationModel>> queryList({
required String eventId,
NotificationFilterCriteria? additionalFilter,
}) {
final filter = NotificationFilterCriteria(
systemParentId: eventId, // Context: current event
...additionalFilter,
);
final params = ListRequestParams<GnotificationFilters, GnotificationOrder>(
filter: filterConvertor.mightExecute(filter),
);
return listDatasource.thenEach(modelConvertor).execute(params);
}

For subscriptions that need filtering context (e.g., chat messages for a specific chat):

Stream<ChatMessageModel> subscribeToMessages({
required String chatId,
required String currentUserId,
}) {
final context = ChatMessageSubscriptionContext(
chatId: chatId,
currentUserId: currentUserId,
);
final params = SubscriptionRequestParams(context: context);
return subscriptionDatasource.thenMap(modelConvertor).execute(params);
}

The subscription context is serialized to JSON and passed via Ferry’s updateCacheHandlerContext:

@riverpod
Convertor<GSubscribeToChatMessagesReq,
SubscriptionRequestParams<ChatMessageSubscriptionContext>>
chatMessageSubscriptionConvertor(Ref ref) {
return Convertor((params) {
return GSubscribeToChatMessagesReq((builder) {
builder
..requestId = 'chat_message_subscription'
..updateCacheHandlerKey = 'messageSubscriptionCacheHandler'
..updateCacheHandlerContext = params.context?.toJson();
});
});
}

Add logging or analytics without modifying the data flow:

final loggedDatasource = datasource.intercept(
onInput: (params) => log('Fetching with context: ${params.filter}'),
onOutput: (data) => log('Received ${data.length} items'),
);

Apply context-dependent behavior:

final withErrorHandling = datasource.decorate((convertor, input) {
try {
return convertor.execute(input);
} catch (e, st) {
analytics.trackError('datasource_error', error: e);
return Stream.error(DomainException('Failed to fetch data'), st);
}
});