sonic 1.3.0
sonic: ^1.3.0 copied to clipboard
A fluent interface for handling network requests.
⚡ Sonic
An HTTP Client with a fluent interface and improved type support.
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
- 🧭 Per-request API versioning with
{baseUrl}
placeholders
📦 Install #
Use your preferred tool:
# Dart
dart pub add sonic
# Flutter
flutter pub add sonic
🚀 Getting Started #
See the Quickstart in the Wiki: Getting Started
🔧 Usage #
Minimal example:
// Create the client (initialize() is called automatically in this ctor)
final sonic = Sonic.initialize(
baseConfiguration: const BaseConfiguration(baseUrl: 'https://api.example.com'),
);
// Typed GET with a decoder
final res = await sonic
.create<User>(url: '/users/1')
.withMethod(HttpMethod.get)
.withDecoder((j) => User.fromJson(j as Map<String, dynamic>))
.execute();
if (res.isSuccess) {
final user = res.data; // User
}
📚 Wiki #
- 📘 Getting Started
- 🗃️ Caching
- 🔁 Retries
- 🤝 Deduplication
- 🔎 Observability
- 🧩 Decoding (Strict, Guards, Adapters)
- 🔐 Auth Refresh
- 📤 Uploads
- 📊 Response Metrics
- 🧭 API Versioning
- ❓ FAQ
- 📚 Cookbook
Deep dives are available in the wiki:
- Pagination
- Circuit breaker and health probe
- Templates
- Stage timers and response metrics
- Rate limiting and priorities
🧭 API Versioning #
If you work with multiple API versions from the same client, configure versioned base URLs and select the version per request:
final sonic = Sonic.initialize(
baseConfiguration: const BaseConfiguration(
baseUrl: 'https://api.example.com',
apiVersions: {
// You can avoid repetition with the {baseUrl} placeholder
'v1': '{baseUrl}/v1',
'v2': '{baseUrl}/v2',
},
),
);
// This request goes to https://api.example.com/v2/users
final res = await sonic
.create<User>(url: '/users')
.withApiVersion('v2')
.withMethod(HttpMethod.get)
.withDecoder((j) => User.fromJson(j as Map<String, dynamic>))
.execute();
When no version is selected (or the key is unknown), the default baseUrl
is used.
Supported placeholders inside apiVersions
values: {baseUrl}
, {origin}
, {scheme}
, {host}
, {port}
, {basePath}
, {version}
.
Advanced: You can also resolve {env:NAME}
via optional flags in BaseConfiguration
:
envPlaceholdersFromDartDefines
: readsString.fromEnvironment(NAME)
envPlaceholdersFromProcessEnv
: readsPlatform.environment[NAME]
See the Wiki: API Versioning
📝 Notes #
- You can bypass type parsing by using
withRawRequest()
and typing asdynamic
. - Decoders are cached per type; after first registration, you typically don’t need to pass them again.
- URLs can be relative (resolved against baseUrl) or absolute.
- If
debugMode
is true, aLogInterceptor
is added to the internal Dio client to log requests/responses. - You can have multiple
Sonic
instances, but a single shared instance via DI is recommended for most apps. - For uploads, use multipart
FormData
andMultipartFile
(see the Uploads wiki page).
🧪 Example #
🔍 Check a runnable example in example/sonic_example.dart
.
📄 License #
📄 MIT © Arun Prakash. See LICENSE
.
🙌 Contributions #
Contributions are always welcome!