Skip to content

API and Components

This section details the primary extensions for chaining and manipulating Convertors, along with the concrete Datasource Convertor implementations.

The architecture provides various extensions to enable pipeline functionalities for different Convertor types.

Basic Convertor Extensions (Convertor<To, From>)

Section titled “Basic Convertor Extensions (Convertor<To, From>)”
Extension MethodPurpose
mightExecuteAllows execution with nullable input, returning a nullable output.
decorateWraps the Convertor with a custom decorator function for input/output modification.
thenChains another Convertor where the current output (To) becomes the new Convertor’s input.
thenConvertChains a simple function that converts the output (To) to a new type (NewTo).
interceptWithIntercepts the input or output using explicit Interceptor objects for side effects.
interceptIntercepts the input or output using simple functions for side effects.

Iterable Convertor Extensions (Convertor<Iterable<To>, From>)

Section titled “Iterable Convertor Extensions (Convertor<Iterable<To>, From>)”
Extension MethodPurpose
thenEachApplies a subsequent Convertor to each item in the output iterable.
thenConvertEachApplies a subsequent conversion function to each item in the output iterable.

StreamConvertor Extensions (StreamConvertor<To, From>)

Section titled “StreamConvertor Extensions (StreamConvertor<To, From>)”

These extensions operate on the data within the output stream.

Extension MethodPurpose
transformApplies a StreamTransformer to the output stream.
mapMaps the data within the output stream to a new type.
thenMapApplies a basic Convertor to the data within the output stream.
asyncMapAsynchronously maps the data within the output stream to a new type.

AsyncConvertor Extensions (AsyncConvertor<To, From>)

Section titled “AsyncConvertor Extensions (AsyncConvertor<To, From>)”

These extensions operate on the result of the Future.

Extension MethodPurpose
thenMapApplies a basic Convertor to the Future’s result (To).
mapMaps the result of the Future synchronously.
asyncMapMaps the result of the Future asynchronously (returns a flattened Future).

Datasource Convertors define the specific logic for fetching and processing data from external sources.

This convertor uses the Dio client for HTTP requests.

  • Input: RestParams (path, method, queryParameters, data, etc.).
class RestParams {
final String path;
final String method;
final Map<String, dynamic>? queryParameters;
final Object? data;
final FormData? formData;
final Options? options;
final CancelToken? cancelToken;
final ProgressCallback? onSendProgress;
final ProgressCallback? onReceiveProgress;
}
  • Executor: RestJsonExecutor<Result> handles the raw HTTP execution.
class RestJsonExecutor<Result> implements AsyncConvertor<Response<Result>, RestParams> {
final Dio dio;
const RestJsonExecutor({required this.dio});
@override
Future<Response<Result>> execute(RestParams argument) async {
var options = argument.options ?? Options();
options = options.copyWith(method: argument.method);
final response = await dio.request<Result>(
argument.path,
data: argument.data ?? argument.formData,
queryParameters: argument.queryParameters,
options: options,
cancelToken: argument.cancelToken,
onReceiveProgress: argument.onReceiveProgress,
onSendProgress: argument.onSendProgress,
);
return response;
}
}
  • Data Conversion: RestDataExecutor<Data> wraps the executor and applies an internal Convertor<Data, dynamic> to transform the raw JSON response data (response.data!) into a domain Data model.
class RestDataExecutor<Data> implements AsyncConvertor<Response<Data>, RestParams> {
final RestJsonExecutor _executor;
final Convertor<Data, dynamic> _convertor;
const RestDataExecutor({
required RestJsonExecutor executor,
required Convertor<Data, Json> convertor,
}) : _executor = executor,
_convertor = convertor;
@override
Future<Response<Data>> execute(RestParams from) async {
final response = await _executor.execute(from);
if (response.data == null) {
throw DioException(
requestOptions: response.requestOptions,
type: DioExceptionType.badResponse,
stackTrace: StackTrace.current,
response: response,
message: response.statusMessage,
);
}
return Response(
data: _convertor.execute(response.data!),
statusCode: response.statusCode,
requestOptions: response.requestOptions,
);
}
}

Or we could also chain the convertors together using the extension:

AsyncConvertor<NewTo, From> AsyncConvertor<To, From>.thenMap<NewTo>(
Convertor<NewTo, To> convertor
)

This convertor uses the Ferry client for GraphQL operations.

  • Input:
class ListRequestParams<F, O> {
final int? skip;
final int? take;
final F? filter;
final O? sort;
}
class SingleRequestParams<F> {
final F? filter;
}
class UpsertRequestParams<V> {
final V vars;
}
class DeleteRequestParams<F> {
final F? filter;
}
class SubscriptionRequestParams<C> {
final C? context;
}
  • Request Executor (GraphQLRequestExecutor): Implements StreamConvertor<OperationResponse<Data, Vars>, Params>.
    • It uses a request-specific Convertor to transform custom Params into a Ferry-compatible OperationRequest.
    • It executes the request via a GqlClient, which returns a Stream<OperationResponse>.
class GraphQLRequestExecutor<Data, Params, Vars>
implements StreamConvertor<OperationResponse<Data, Vars>, Params> {
final GqlClient _gqlClient;
final Convertor<OperationRequest<Data, Vars>, Params> _convertor;
const GraphQLRequestExecutor({
required GqlClient gqlClient,
required Convertor<OperationRequest<Data, Vars>, Params> convertor,
}) : _gqlClient = gqlClient,
_convertor = convertor;
@override
Stream<OperationResponse<Data, Vars>> execute(Params from) {
final request = _convertor.execute(from);
return _gqlClient.request(request);
}
}
  • Stream Convertor (GraphQLStreamConvertor): Implements StreamConvertor<Data, Stream<OperationResponse<Data, Vars>>>.
    • This component chains onto the Executor to transform the stream of OperationResponse into a stream of the actual GQL data (Data).
    • It handles unwrapping the data and propagating GraphQL or Link errors.
class GraphQLStreamConvertor<Data, Vars>
implements StreamConvertor<Data, Stream<OperationResponse<Data, Vars>>> {
const GraphQLStreamConvertor();
@override
Stream<Data> execute(Stream<OperationResponse<Data, Vars>> from) {
return from.transform(
StreamTransformer.fromHandlers(
handleData: (data, sink) {
if (data.hasErrors || data.data == null) {
sink.addError(
data.graphqlErrors?.firstOrNull ??
data.linkException ??
Exception(),
);
} else {
sink.add(data.data!);
}
},
),
);
}
}