sonic 1.0.0 copy "sonic: ^1.0.0" to clipboard
sonic: ^1.0.0 copied to clipboard

A fluent interface for handling network requests.

⚡ Sonic

An HTTP Client with a fluent interface and improved type support.

pub version pub points license MIT API Reference

Blazingly simple, production-ready networking on top of Dio — with smart caching, retries, dedup, and observability.


✨ Features #

  • ✨ Fluent interface and typed decoders (cached per type)
  • 🔁 Retries with backoff, jitter, and Retry-After; 429-aware
  • 🧠 Caching: in-memory TTL+LRU, SWR, ETag/Last-Modified/Expires
  • 🤝 Request deduplication for identical in-flight GETs
  • 🧑‍💻 Per-host rate limiting (token bucket) with priority
  • 🛡️ Circuit breaker per host with event hooks
  • 🧩 Templates for reusable request presets
  • 📊 Observability: detailed response.extra metrics
  • � Fluent uploads (multipart, fields/files)
  • 📄 Pagination utilities (Link headers, cursors, adapters, Paged

📦 Install #

Use your preferred tool:

# Dart
dart pub add sonic

# Flutter
flutter pub add sonic

🚀 Getting Started #

See the Quickstart in the Wiki: https://github.com/ArunPrakashG/sonic/wiki/Getting-Started

🔧 Usage #

See examples and patterns in the Wiki pages linked below.

📤 Uploads (fluent) #

await sonic
  .create<void>(url: '/upload')
  .upload() // multipart + POST
  .field('userId', '123')
  .fileFromPath('file', '/path/to/file.txt', filename: 'file.txt')
  .onUploadProgress((p) => print('sent \\${p.current}/\\${p.total}'))
  .execute();

More examples: https://github.com/ArunPrakashG/sonic/wiki/Uploads

📚 Wiki #

The main README is intentionally minimal. Full docs live in the GitHub Wiki:

If your API returns RFC Link headers, you can build next/prev requests directly from a response:

final res = await sonic.create<List<Post>>(url: '/posts')
  .withDecoder((j) => (j as List).map((e) => Post.fromJson(e)).toList())
  .execute();

final next = res.nextPage(sonic);
final res2 = await next?.withDecoder((j) => (j as List).map((e) => Post.fromJson(e)).toList()).execute();

For cursor in JSON body instead of headers, you can still use pluck()/envelope() and construct URLs yourself.

Or, build a strongly-typed page using toPaged/makePaged:

final res = await sonic
    .create<Map<String, dynamic>>(url: '/posts?limit=10')
    .execute();

final paged = res.toPaged<Post>(
  itemDecoder: (j) => Post.fromJson(j as Map<String, dynamic>),
  itemsPath: 'data',
  nextCursorPath: 'meta.nextCursor',
  prevCursorPath: 'meta.prevCursor',
  cursorParam: 'cursor',
);

for (final item in paged.items) {
  // ...
}

final nextRef = paged.next; // has url and a list decoder

🛡️ Circuit breaker events #

Provide an event hook via BaseConfiguration to observe state changes per host:

final sonic = Sonic.initialize(
  baseConfiguration: BaseConfiguration(
    baseUrl: 'https://api.example.com',
    circuitBreakerConfig: const CircuitBreakerConfig(failureThreshold: 5),
    onCircuitEvent: (host, state) {
      print('Circuit for $host => $state');
    },
  ),
);

🧩 Templates to reduce repetition #

sonic.registerTemplate('authJsonPost', (b) {
  b.post().withHeader('Content-Type','application/json');
});

final builder = sonic.template<User>('authJsonPost', url: '/users')
  .withDecoder((j) => User.fromJson(j));

⏱️ Stage timers (response.extra) #

SonicResponse.extra includes useful timing data when available:

  • durationMs: total end-to-end time for the request
  • cacheLookupMs: time spent checking cache
  • cacheRevalidateMs: background revalidation time (SWR)
  • networkMs: time for the network call
  • rateLimitWaitMs: time spent waiting on rate limiter
  • decodeMs: time to decode payload

Rate limiting and priority #

Configure per-host rate limits via BaseConfiguration:

final sonic = Sonic.initialize(
  baseConfiguration: BaseConfiguration(
    baseUrl: 'https://api.example.com',
    rateLimits: {
      'api.example.com': const RateLimitConfig(permitsPerSecond: 10, burst: 20),
    },
  ),
);

// Mark important requests as high priority to reduce wait time under backpressure
final res = await sonic
    .create<String>(url: '/data')
    .withPriority(RequestPriority.high)
    .execute();

HTTP 429 responses are considered retryable by default and Sonic respects the Retry-After header when computing backoff.

📊 Response metrics #

📊 Every SonicResponse includes optional extra metadata to help with diagnostics and tuning:

  • ⏱️ durationMs: total wall time for the request path (including cache checks)
  • 🗃️ cache: one of network, store, or validated-304
  • 🏷️ cacheEvent: network, hit, stale, or validated
  • 🔁 retries: number of retry attempts performed
  • ⌛ retryDelaysMs: list of delays applied between attempts
  • 🤝 deduplicated: whether this response came from a deduplicated in-flight GET

📝 Notes #

  • 🧪 You can also get raw response and ignore the type parsing by using asRawRequest() builder method and specifying the type parameter as dynamic
  • 🧠 You wont need to pass decoder using withDecoder after the first call to the same type as the decoder will be cached based on the type.
  • 🌐 You can either pass relative url (path) or an absolute url to the url named parameter.
  • 🐞 If debugMode is true, LogInterceptor will be added to the internal Dio instance and it will be used to log everything related to a network call in the standard output.
  • 🧩 You can have multiple instances of Sonic as individually, they do not share resources with each other. But it is recommended to have a global instance of Sonic and use dependency injection to inject them into your business logic.
  • 📦 You can use FormData as body for uploading files etc. You can wrap the file with MultipartFile instance. these are from Dio library.

🧪 Example #

🔍 Check a runnable example in example/sonic_example.dart.

📄 License #

📄 MIT © Arun Prakash. See LICENSE.

🙌 Contributions #

🙌 Contributions are always welcome!

2
likes
0
points
7
downloads

Publisher

verified publisherarunprakashg.com

Weekly Downloads

A fluent interface for handling network requests.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

collection, dio, http_parser, meta, path, synchronized

More

Packages that depend on sonic