Response Parser makes it easier to parse data and error response from server.

Overview

Response Parser makes it easier to parse data and error response from server.

Getting started

Do you want to write this pretty functions...

Future<Either<User, ApiFailure>> fetchUser() async {
  return parseApiResponse(
    requestAction: () => dio.get('/user'),
    mapper: User.fromJson,
  );
}

...instead of this boring code?

Future<Either<User, ApiFailure>> fetchUser() async {
  final dio = Dio(BaseOptions(baseUrl: 'https://example.com'));
  try {
    final request = await dio.get('/user');
    final data = request.data?['data'];
    if (data == null) {
      final error = request.data?['error'];
      if (error != null) {
        return ApiFailure.serverFailure(error['message']);
      } else {
        return ApiFailure.unknown();
      }
    } else {
      return User.fromJson(data);
    }
  } catch (error, st) {
    ApiFailure? apiFailure;
    if (error is DioError) {
      final responseFailure = error.response?.data;
      if (responseFailure is Map<String, dynamic>) {
        apiFailure = ApiFailure.serverFailure(responseFailure['message']);
      } else {
        apiFailure = ApiFailure.httpError(error.response?.statusCode);
      }
    }
    return apiFailure ?? ApiFailure.unknown();
  }
}

Then continue reading!

Usage

To do so you need to do a little preparation.
For example lets assume your server returns such response:

{
  "data": {
    // Data you requested
  },
  "error": {
    // Server error which you should parse and show to user
    "message": "Something went wrong"
  }
}

And your error model looks this way:

class ApiFailure {
  factory ApiFailure.unknown() = _UnknownApiFailure;
  factory ApiFailure.serverFailure(String errorMessage) = _ServerFailure;
  factory ApiFailure.httpError(int? statusCode) = _HttpError;
}

Then you need to implement dataExtractor, failureParser and errorCatcher this way:

final _exampleResponseParser = ResponseParser<Response, ApiFailure>(
  dataExtractor: (response) => response.data['data']!,
  failureParser: (response) {
    final error = json['error'];
    if (error is Map<String, dynamic>) {
      return ApiFailure.serverFailure(error['message']);
    } else {
      return null;
    }
  },
  errorCatcher: (error, stackTrace) {
    ApiFailure? apiFailure;
    if (error is DioError) {
      apiFailure = ApiFailure.httpError(error.response?.statusCode);
    }
    return apiFailure ?? ApiFailure.unknown();
  },
);

And create top level parseApiResponse, parseListApiResponse and parseEmptyApiResponse functions.

final parseApiResponse = _exampleResponseParser.parseApiResponse;
final parseListApiResponse = _exampleResponseParser.parseListApiResponse;
final parseEmptyApiResponse = _exampleResponseParser.parseEmptyApiResponse;

That's all!
For more info you can take a look at example.

You might also like...

🧾 Flutter widget allowing easy cache-based data display in a ListView featuring pull-to-refresh and error banners.

Often, apps just display data fetched from some server. This package introduces the concept of fetchable streams. They are just like normal Streams, b

Jan 18, 2022

Powerful, helpfull, extensible and highly customizable API's that wrap http client to make communication easier with Axelor server with boilerplate code free.

Powerful, helpfull, extensible and highly customizable API's that wrap http client to make communication easier with Axelor server with boilerplate code free.

flutter_axelor_sdk Powerful, helpful, extensible and highly customizable API's that wrap http client to make communication easier with Axelor server w

Dec 25, 2022

D info - Flutter package for response info message

D info - Flutter package for response info message

D'Info Flutter package for response info message. It's like bootstrap view but s

Oct 26, 2022

Data Migrator - provide a universal translator for data by being portable, diverse, and efficient in migrating and converting data across discrete schemas

Data Migrator - provide a universal translator for data by being portable, diverse, and efficient in migrating and converting data across discrete schemas

Data Migrator - provide a universal translator for data by being portable, diverse, and efficient in migrating and converting data across discrete schemas

Jan 2, 2023

A Dart EPUB parser built from the ground up, and designed to support a variety of use cases and custom

A Dart EPUB parser built from the ground up, and designed to support a variety of use cases and custom implementations such as on-device caching and serving content from a server.

Nov 3, 2022

Nexus is a state management library that makes it easy to create and consume your application's reactive data to the user interface.

Nexus πŸš€ Nexus is a state management library that makes it easy to create and consume your application's reactive data to the user interface. With nex

Sep 7, 2022

Receipt parser application written in dart.

Receipt parser application written in dart.

Receipt manager You can find pre-compiled releases on the Github release page or in the FDROID repository. All the needed info about how to use the re

Dec 29, 2022

Fan-made, handmade, recursive-descent parser for the Dart programming language.

Very Unofficial Parser Fan-made, handmade, recursive-descent parser for the Dart programming language. Although this parser strives to parse the langu

Nov 21, 2022

App HTTP Client is a wrapper around the HTTP library Dio to make network requests and error handling simpler, more predictable, and less verbose.

App HTTP Client App HTTP Client is a wrapper around the HTTP library Dio to make network requests and error handling simpler, more predictable, and le

Nov 1, 2022
Comments
  • Bump fpdart from 0.2.0 to 0.3.0

    Bump fpdart from 0.2.0 to 0.3.0

    Bumps fpdart from 0.2.0 to 0.3.0.

    Release notes

    Sourced from fpdart's releases.

    v0.3.0

    • Inverted onSome and onNone functions parameters in match method of Option [⚠️ BREAKING CHANGE] (Read more on why πŸ‘‰ #56)
    /// Everywhere you are using `Option.match` you must change this:
    final match = option.match(
      (a) => print('Some($a)'),
      () => print('None'), // <- `None` second πŸ‘Ž 
    );
    

    /// to this (invert parameters order): final match = option.match( () => print('None'), // <- None first πŸ‘ (a) => print('Some($a)'), );

    • Added traverse and sequence methods (#55)
      • traverseList
      • traverseListWithIndex
      • sequenceList
      • traverseListSeq
      • traverseListWithIndexSeq
      • sequenceListSeq
    /// "a40" is invalid πŸ’₯
    final inputValues = ["10", "20", "30", "a40"];
    

    /// Verify that all the values can be converted to [int] πŸ” /// /// If any of them is invalid, then the result is [None] πŸ™…β€β™‚οΈ final traverseOption = inputValues.traverseOption( (a) => Option.tryCatch( /// If a does not contain a valid integer literal a [FormatException] is thrown () => int.parse(a), ), );

    • Added bindEither method in TaskEither (#58)
    /// Chain [Either] to [TaskEither]
    TaskEither<String, int> binding =
        TaskEither<String, String>.of("String").bindEither(Either.of(20));
    
    • Added lefts, rights, and partitionEithers methods to Either (#57)
    final list = [
      right<String, int>(1),
      right<String, int>(2),
      left<String, int>('a'),
      left<String, int>('b'),
      right<String, int>(3),
    </tr></table> 
    

    ... (truncated)

    Changelog

    Sourced from fpdart's changelog.

    v0.3.0 - 11 October 2022

    • Inverted onSome and onNone functions parameters in match method of Option [⚠️ BREAKING CHANGE] (Read more on why πŸ‘‰ #56)
    /// Everywhere you are using `Option.match` you must change this:
    final match = option.match(
      (a) => print('Some($a)'),
      () => print('None'), // <- `None` second πŸ‘Ž 
    );
    

    /// to this (invert parameters order): final match = option.match( () => print('None'), // <- None first πŸ‘ (a) => print('Some($a)'), );

    • Added traverse and sequence methods (#55)
      • traverseList
      • traverseListWithIndex
      • sequenceList
      • traverseListSeq
      • traverseListWithIndexSeq
      • sequenceListSeq
    /// "a40" is invalid πŸ’₯
    final inputValues = ["10", "20", "30", "a40"];
    

    /// Verify that all the values can be converted to [int] πŸ” /// /// If any of them is invalid, then the result is [None] πŸ™…β€β™‚οΈ final traverseOption = inputValues.traverseOption( (a) => Option.tryCatch( /// If a does not contain a valid integer literal a [FormatException] is thrown () => int.parse(a), ), );

    • Added bindEither method in TaskEither (#58)
    /// Chain [Either] to [TaskEither]
    TaskEither<String, int> binding =
        TaskEither<String, String>.of("String").bindEither(Either.of(20));
    
    • Added lefts, rights, and partitionEithers methods to Either (#57)
    final list = [
      right<String, int>(1),
      right<String, int>(2),
      left<String, int>('a'),
      left<String, int>('b'),
      right<String, int>(3),
    </tr></table> 
    

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 1
  • Bump fpdart from 0.2.0 to 0.4.0

    Bump fpdart from 0.2.0 to 0.4.0

    Bumps fpdart from 0.2.0 to 0.4.0.

    Release notes

    Sourced from fpdart's releases.

    v0.4.0

    • Added extension methods to work with nullable types (T?)
      • From T? to fpdart's types
        • toOption
        • toEither
        • toTaskOption
        • toIOEither
        • toTaskEither
        • toTaskEitherAsync
        • fromNullable (Either, IOEither, TaskOption TaskEither)
        • fromNullableAsync (TaskEither)
      • From fpdart's types to T?
        • toNullable (Either)
    /// [Option] <-> `int?`
    int? value1 = 10.toOption().map((t) => t + 10).toNullable();
    

    bool? value2 = value1?.isEven;

    /// bool? -> [Either] -> int? int? value3 = value2 .toEither(() => "Error") .flatMap((a) => a ? right<String, int>(10) : left<String, int>("None")) .toNullable();

    /// int? -> [Option] Option<int> value4 = (value3?.abs().round()).toOption().flatMap(Option.of);

    • Added toIOEither to Either
    • Removed parameter from Either fromNullable [⚠️ BREAKING CHANGE]
    final either = Either<String, int>.fromNullable(value, (r) => 'none');
    

    /// πŸ‘† Removed the value (r) (it was always null anyway πŸ’πŸΌβ€β™‚οΈ) πŸ‘‡

    final either = Either<String, int>.fromNullable(value, () => 'none');

    • Added chainEither to TaskEither
    • Added safeCast (Either and Option)
    • Added safeCastStrict (Either and Option)
    int intValue = 10;
    

    /// Unhandled exception: type 'int' is not a subtype of type 'List<int>' in type cast final waitWhat = intValue as List<int>; final first = waitWhat.first;

    /// Safe 🎯 final wellYeah = Either<String, List<int>>.safeCast( intValue, </tr></table>

    ... (truncated)

    Changelog

    Sourced from fpdart's changelog.

    v0.4.0 - 16 December 2022

    • Added extension methods to work with nullable types (T?)
      • From T? to fpdart's types
        • toOption
        • toEither
        • toTaskOption
        • toIOEither
        • toTaskEither
        • toTaskEitherAsync
        • fromNullable (Either, IOEither, TaskOption TaskEither)
        • fromNullableAsync (TaskEither)
      • From fpdart's types to T?
        • toNullable (Either)
    /// [Option] <-> `int?`
    int? value1 = 10.toOption().map((t) => t + 10).toNullable();
    

    bool? value2 = value1?.isEven;

    /// bool? -> [Either] -> int? int? value3 = value2 .toEither(() => "Error") .flatMap((a) => a ? right<String, int>(10) : left<String, int>("None")) .toNullable();

    /// int? -> [Option] Option<int> value4 = (value3?.abs().round()).toOption().flatMap(Option.of);

    • Added toIOEither to Either
    • Removed parameter from Either fromNullable [⚠️ BREAKING CHANGE]
    final either = Either<String, int>.fromNullable(value, (r) => 'none');
    

    /// πŸ‘† Removed the value (r) (it was always null anyway πŸ’πŸΌβ€β™‚οΈ) πŸ‘‡

    final either = Either<String, int>.fromNullable(value, () => 'none');

    • Added chainEither to TaskEither
    • Added safeCast (Either and Option)
    • Added safeCastStrict (Either and Option)
    int intValue = 10;
    

    /// Unhandled exception: type 'int' is not a subtype of type 'List<int>' in type cast final waitWhat = intValue as List<int>; final first = waitWhat.first;

    /// Safe 🎯 final wellYeah = Either<String, List<int>>.safeCast( intValue, </tr></table>

    ... (truncated)

    Commits

    Dependabot compatibility score

    Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


    Dependabot commands and options

    You can trigger Dependabot actions by commenting on this PR:

    • @dependabot rebase will rebase this PR
    • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
    • @dependabot merge will merge this PR after your CI passes on it
    • @dependabot squash and merge will squash and merge this PR after your CI passes on it
    • @dependabot cancel merge will cancel a previously requested merge and block automerging
    • @dependabot reopen will reopen this PR if it is closed
    • @dependabot close will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
    • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
    • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)
    dependencies 
    opened by dependabot[bot] 0
Owner
Qyre AB
Qyre AB
An intuitive Token Parser that includes grammar definition, tokenization, parsing, syntax error and debugging. Implementation based on Lexical Analysis for Dart.

Token Parser An intuitive Token Parser that includes syntax/grammar definition, tokenization and parsing. Implementation based on Lexical Analysis. Re

JUST A SNIPER ツ 2 Dec 15, 2022
Dart duration iso parser - Package to parse duration from ISO 8601 string

duration_iso_parser Package for parsing ISO 8601 durations string to the Duratio

Innim 1 Jan 18, 2022
Datacollection - This will help to generate the data collection in *.g.dart file from api response

datacollection this will help to generate the data collection in *.g.dart file f

ke chankrisna 2 Feb 5, 2022
A Flutter plugin that allows communication with a Parse Server

Parse For Flutter! Hi, this is a Flutter plugin that allows communication with a Parse Server, (https://parseplatform.org) either hosted on your own s

Doğuş Dicle 0 Apr 14, 2022
This package helps developer to sort the flutter/dart packages and plugins alphabetically, This makes it easier when managing too many packages and when working with teams

Package helps to sort the flutter/dart packages and plugins alphabetically, This makes it easier when managing too many packages and when working with

DANCHE 7 Dec 21, 2022
A draggable Flutter widget that makes implementing a Sliding up and fully-stretchable much easier.

Draggable Home A draggable Flutter widget that makes implementing a Sliding up and fully-stretchable much easier! Based on the Scaffold and Sliver. Us

Devs On Flutter 106 Dec 12, 2022
A set of Flutter widgets that makes grouping Checkboxes and Radio Buttons much easier!

grouped_buttons A set of Flutter widgets that makes grouping Checkboxes and Radio Buttons much easier! Installing Add the following to your pubspec.ya

Akshath Jain 102 Dec 28, 2022
πŸ’™πŸ”₯ FlutterFire commons repository, makes FlutterFire app development faster, easier, and more fun!

???? FlutterFire commons repository, makes FlutterFire app development faster, easier, and more fun!

Kosuke Saigusa 18 Dec 1, 2022
Nebula makes your Flutter development journey easier by providing helper widgets, utilities and abstractions.

Nebula makes your Flutter development journey easier by providing helper widgets, utilities and abstractions.

Aldrin's Art Factory 1 Apr 21, 2022
A platform similar to iFood that makes it easier for the consumers to find a list of generators with solar plates system available for rent

iEnergy App A platform similar to iFood that makes it easier for the consumers to find a list of generators with solar plates system available for ren

AndrΓ© Diogo 1 Jun 7, 2022