arch-atlas

Server failure part 3

Data Layer

13:54

Summary

This lesson completes the implementation of the ServerFailure class by addressing the DioExceptionType.badResponse case. It introduces a dedicated factory constructor to evaluate HTTP status codes and extract specific error messages directly from the API's JSON response body. By parsing common client and server status codes (such as 400, 401, 403, 404, and 500) and translating them into user-friendly messages, the data layer safely encapsulates API-specific error formats so the UI doesn't have to deal with raw HTTP responses.

Key Ideas

  • DioExceptionType.badResponse triggers when a request successfully reaches the server, but the server rejects it with an error status code.
  • A dedicated factory constructor (fromResponse) isolates the parsing logic for different HTTP status codes.
  • Status codes 404 and 500 usually imply routing or internal server errors, making it safer to return hardcoded, generic messages rather than relying on the response payload.
  • Status codes 400, 401, and 403 often come with structured JSON body payloads that contain detailed validation or authorization error messages.
  • The structure for accessing the error string (e.g., response['error']['message']) is strictly coupled to the specific API documentation being consumed.
  • Unhandled general exceptions inside the Repository are caught via a standard catch (e) block and mapped to a generic ServerFailure(e.toString()).

Code Snippets

Final implementation of ServerFailure parsing Dio errors and API responsesdart
abstract class Failure {
  final String message;

  Failure(this.message);
}

class ServerFailure extends Failure {
  ServerFailure(super.message);

  factory ServerFailure.fromDioError(DioError e) {
    switch (e.type) {
      case DioErrorType.connectionTimeout:
        return ServerFailure('Connection timeout with api server');
      case DioErrorType.sendTimeout:
        return ServerFailure('Send timeout with ApiServer');
      case DioErrorType.receiveTimeout:
        return ServerFailure('Receive timeout with ApiServer');
      case DioErrorType.badCertificate:
        return ServerFailure('badCertificate with api server');
      case DioErrorType.badResponse:
        return ServerFailure.fromResponse(e.response!.statusCode!, e.response!.data);
      case DioErrorType.cancel:
        return ServerFailure('Request to ApiServer was canceld');
      case DioErrorType.connectionError:
        return ServerFailure('No Internet Connection');
      case DioErrorType.unknown:
        return ServerFailure('Opps There was an Error, Please try again');
    }
  }

  factory ServerFailure.fromResponse(int statusCode, dynamic response) {
    if (statusCode == 404) {
      return ServerFailure('Your request was not found, please try later');
    } else if (statusCode == 500) {
      return ServerFailure('There is a problem with server, please try later');
    } else if (statusCode == 400 || statusCode == 401 || statusCode == 403) {
      return ServerFailure(response['error']['message']);
    } else {
      return ServerFailure('There was an error, please try again');
    }
  }
}
Updated catch block inside home_repo_impl.dart to handle non-Dio exceptionsdart
      } catch (e) {
        if (e is DioError) {
          return left(ServerFailure.fromDioError(e));
        }
        return left(ServerFailure(e.toString()));
      }

My Notes

  • The instructor hardcodes the error parsing logic (response['error']['message']) directly inside the ServerFailure class. While this works for the Google Books API, it tightly couples the core Domain/Data error classes to a single external service's JSON format. In larger apps with multiple APIs, this logic should ideally be abstracted or handled closer to the specific Data Source.
  • [Editor's note] Using dynamic for the API response payload is a common shortcut but introduces a major vulnerability. If a 400 error returns a plain text string instead of JSON, response['error'] will throw a NoSuchMethodError and crash the app. A safer implementation would explicitly check the type: if (response is Map<String, dynamic>) { ... }.
  • [Editor's note] DioError is used throughout, but keep in mind that modern projects using up-to-date Dio packages must use DioException.