intercepted_http 0.1.0 copy "intercepted_http: ^0.1.0" to clipboard
intercepted_http: ^0.1.0 copied to clipboard

A composable HTTP interceptor layer for package:http. Add auth headers, token refresh, retry logic, and logging without replacing your HTTP client.

intercepted_http #

A composable interceptor layer for package:http.
Add auth headers, logging, token refresh, and retry logic — without replacing your HTTP client.

final client = InterceptedHttp(
  interceptors: [LoggingInterceptor(), AuthInterceptor()],
);

// Use it like any http.Client
final response = await client.get(Uri.parse('https://api.example.com/users'));

Why #

package:http is the standard Dart HTTP client, but it has no built-in way to intercept requests. Every project ends up copy-pasting the same boilerplate for auth headers, token refresh, and logging.

intercepted_http solves this once with a clean interceptor API that works for Flutter, server-side Dart, and CLI tools — no framework lock-in.

Installation #

dependencies:
  intercepted_http: ^0.1.0

Quick start #

import 'package:intercepted_http/intercepted_http.dart';

final client = InterceptedHttp(
  interceptors: [MyInterceptor()],
  timeout: Duration(seconds: 30),
);

final response = await client.get(Uri.parse('https://api.example.com/todos'));
print(response.statusCode);

client.close();

Writing interceptors #

Extend HttpInterceptor and override only the hooks you need:

class AuthInterceptor extends HttpInterceptor {
  @override
  Future<void> onRequest(http.Request request) async {
    request.headers['Authorization'] = 'Bearer ${await getToken()}';
  }
}

Available hooks #

Hook When it runs
onRequest Before the request is sent. Mutate headers, sign the request.
onResponse After every response — any status code.
onError Only when statusCode >= 400.
shouldRetry On network exceptions or after onError. Return true to retry.

Token refresh on 401 #

class TokenRefreshInterceptor extends HttpInterceptor {
  bool _refreshed = false;

  @override
  Future<void> onError(http.Response response, http.Request request) async {
    if (response.statusCode == 401) {
      final newToken = await refreshToken();         // your refresh logic
      request.headers['Authorization'] = 'Bearer $newToken';
      _refreshed = true;
    }
  }

  @override
  Future<bool> shouldRetry(Object error, StackTrace st, http.Request request,
      {http.Response? response}) async {
    if (response?.statusCode == 401 && _refreshed) {
      _refreshed = false;
      return true;                                   // retry with new token
    }
    return false;
  }
}

Retry on network errors #

class NetworkRetryInterceptor extends HttpInterceptor {
  @override
  Future<bool> shouldRetry(Object error, StackTrace st, http.Request request,
      {http.Response? response}) async {
    return response == null; // only on exceptions, not HTTP errors
  }
}

Logging #

class LoggingInterceptor extends HttpInterceptor {
  @override
  Future<void> onRequest(http.Request request) async {
    print('→ ${request.method} ${request.url}');
  }

  @override
  Future<void> onResponse(http.Response response, http.Request request) async {
    print('← ${response.statusCode} ${request.url}');
  }
}

Configuration #

InterceptedHttp(
  interceptors: [...],

  // Inner client — use IOClient for mTLS, MockClient for tests
  client: IOClient(),

  // Per-request timeout (default: 30s)
  timeout: Duration(seconds: 30),

  // Max retry attempts per request (default: 1)
  maxRetries: 1,

  // Throw HttpClientException on 4xx/5xx (default: true)
  throwOnError: true,
)

Error handling #

When throwOnError: true (default), 4xx/5xx responses throw HttpClientException:

try {
  await client.get(Uri.parse('https://api.example.com/users'));
} on HttpClientException catch (e) {
  print(e.statusCode);       // 404
  print(e.message);          // extracted from {"message": "..."}
  print(e.isUnauthorized);   // true if 401
  print(e.isServerError);    // true if >= 500
}

Set throwOnError: false to handle errors manually via onError interceptor.

Using a custom inner client #

InterceptedHttp wraps any http.Client, so you can combine it with mTLS, proxies, or mocks:

// mTLS
final client = InterceptedHttp(
  client: IOClient(mySecureHttpClient),
  interceptors: [AuthInterceptor()],
);

// Tests
final client = InterceptedHttp(
  client: MockClient((request) async => http.Response('{}', 200)),
  interceptors: [AuthInterceptor()],
);

Interceptor execution order #

Interceptors run in the order they are listed in the interceptors list:

[LoggingInterceptor, AuthInterceptor, RetryInterceptor]
     onRequest:  Logging → Auth → Retry
     onResponse: Logging → Auth → Retry
     onError:    Logging → Auth → Retry
     shouldRetry: Logging → Auth → Retry  (first true wins)

License #

MIT

1
likes
0
points
145
downloads

Publisher

verified publishermatheusvinicius.dev.br

Weekly Downloads

A composable HTTP interceptor layer for package:http. Add auth headers, token refresh, retry logic, and logging without replacing your HTTP client.

Homepage
Repository (GitHub)
View/report issues

Topics

#http #networking #interceptor #middleware

License

unknown (license)

Dependencies

http

More

Packages that depend on intercepted_http