Have you ever felt despair when it was time to integrate your app with yet another RESTful API? This meant writing more boilerplate by creating new ApiManagers and ApiRepositories calling same Dio or Http methods over again.

Danela (Dart Abstract Network Layer) encapsulates the network layer logic providing a modular, customizable, efficient, and intuitive solution. Move from the boilerplate and legacy mess to the general approach.

Danela, the approach, and this image was inspired by Moya.

Installation

# pubspec.yaml
dependencies:
  danela: ^1.0.0

Usage

Danela operates with two data types. They do nothing but define the characteristics of an action. You can compare them to the Flutter widgets that act as a blueprint for the actual UI.

  • Requests describe the action of requesting something from the Internet.
  • RequestMappers describe the way raw response data is processed and mapped to the outcoming model/

As for the actions over data types, there are also two concepts:

  • Gateway is an interface for an object performing requests.
  • Repository is an interface for an object caching or storing the data gateways provide it with.
// Create a request to the Joke API
final request = const Request(
    url: 'https://official-joke-api.appspot.com/random_joke',
);

// Define a mapper producing a String from a Dio's Response (since we are using Dio here)
final mapper = RequestMapper<Response, String>(
    mapJson: Joke.fromJson,
    onError: parseError,
);

// Create the Dio instance
final dio = Dio();

// Now we define the Gateway
final gateway = DioGateway(
    dio: dio,
    request: request,
    mapper: mapper,
);

// Safely fetch the String result
print(await gateway.run());

// Now, let's add caching 
final repository = DefaultRepository(gateway: gateway);

// We're good! Cache usage is set to true by default in the DefaultRepository
print(await repository.run(useCache: true));

Using fpdart

If you're a functional programming approach enjoyer, leverage its beauty and conciseness easily with Danela.

// Use a mapper with Either
final mapper = RequestMapper<Response, Either<String, Joke>>(
    mapJson: (json) => Either.of(Joke.fromJson(json)),
    onError: (e) => '$e',
);

// Define the gateway as usual
final gateway = DioGateway(
    dio: dio,
    request: request,
    mapper: mapper,
);

// Wrap the run with a TaskEither 
final gatewayTask = TaskEither(gateway.run);

// Or define a local extension to transform a Future to the Task
extension GatewayToTaskExt<T> on Gateway<T> {
    Task<T> get toTask => Task(run);
}

Testing

Danela is composable, so feel free to implement a MockGateway or MockRepository and inject it as a repository in your modules.

class MockRepository<T> implements Repository<T> {
    @override
    Future<T> run() => Future.delayed(
        const Duration(seconds: 2), 
        () => Random().nextInt(5),
    );
    
    @override
    void dispose() {}
}

Contacts

The package is in developmen, so in case of problems or wishes to improve the project, feel free to create an issue on GitHub or contact me on Telegram.

Libraries

danela