home / skills / kaakati / rails-enterprise-dev / http-integration
This skill helps you implement robust HTTP integration patterns, including providers, authenticated clients, and structured error handling for reliable APIs.
npx playbooks add skill kaakati/rails-enterprise-dev --skill http-integrationReview the files below or copy the command above to add this skill to your agents.
---
name: "HTTP Integration Patterns"
description: "HTTP client configuration, API providers, error handling, and request/response patterns"
version: "1.0.0"
---
# HTTP Integration Patterns
## HTTP Provider Pattern
```dart
import 'dart:convert';
import 'package:http/http.dart' as http;
class UserProvider {
final http.Client _client;
final String _baseUrl;
UserProvider(this._client, {String? baseUrl})
: _baseUrl = baseUrl ?? 'https://api.example.com';
Map<String, String> get _headers => {
'Content-Type': 'application/json',
'Accept': 'application/json',
};
Future<UserModel> fetchUser(String id) async {
final response = await _client.get(
Uri.parse('$_baseUrl/users/$id'),
headers: _headers,
).timeout(Duration(seconds: 10));
if (response.statusCode == 200) {
return UserModel.fromJson(json.decode(response.body));
} else if (response.statusCode == 404) {
throw ServerException(message: 'User not found');
} else {
throw ServerException(
message: 'Failed to fetch user',
statusCode: response.statusCode,
);
}
}
Future<List<UserModel>> fetchAllUsers() async {
final response = await _client.get(
Uri.parse('$_baseUrl/users'),
headers: _headers,
).timeout(Duration(seconds: 10));
if (response.statusCode == 200) {
final List<dynamic> data = json.decode(response.body);
return data.map((json) => UserModel.fromJson(json)).toList();
} else {
throw ServerException(message: 'Failed to fetch users');
}
}
Future<UserModel> createUser(Map<String, dynamic> data) async {
final response = await _client.post(
Uri.parse('$_baseUrl/users'),
headers: _headers,
body: json.encode(data),
).timeout(Duration(seconds: 10));
if (response.statusCode == 201) {
return UserModel.fromJson(json.decode(response.body));
} else {
throw ServerException(message: 'Failed to create user');
}
}
Future<void> deleteUser(String id) async {
final response = await _client.delete(
Uri.parse('$_baseUrl/users/$id'),
headers: _headers,
).timeout(Duration(seconds: 10));
if (response.statusCode != 204) {
throw ServerException(message: 'Failed to delete user');
}
}
}
```
## Authenticated HTTP Client
```dart
class AuthenticatedClient extends http.BaseClient {
final http.Client _inner;
final String Function() _getToken;
AuthenticatedClient(this._inner, this._getToken);
@override
Future<http.StreamedResponse> send(http.BaseRequest request) async {
final token = _getToken();
if (token.isNotEmpty) {
request.headers['Authorization'] = 'Bearer $token';
}
request.headers['Content-Type'] = 'application/json';
request.headers['Accept'] = 'application/json';
return _inner.send(request);
}
}
// Usage in bindings
Get.lazyPut<http.Client>(
() => AuthenticatedClient(
http.Client(),
() => Get.find<StorageService>().token ?? '',
),
);
```
## Error Handling
```dart
try {
final response = await _client.get(url);
return parseResponse(response);
} on SocketException {
throw NetworkException();
} on TimeoutException {
throw ServerException(message: 'Request timeout');
} on FormatException {
throw ServerException(message: 'Invalid response format');
} catch (e) {
throw ServerException(message: e.toString());
}
```
This skill documents HTTP Integration Patterns for reliable API consumption: client configuration, provider wrappers, authentication injection, and robust error handling. It presents a portable approach you can apply across Rails-backed services or any API-driven project for predictable request/response behavior. The patterns emphasize testability, timeouts, and clear mapping between HTTP status codes and domain errors.
A provider class wraps an HTTP client and exposes typed methods (fetch, create, delete) that build URIs, set JSON headers, apply timeouts, and parse responses into domain models. An authenticated client decorates requests with Authorization headers and standard content headers before forwarding to an inner client. Centralized error mapping converts networking, timeout, and format errors into domain-specific exceptions so calling code handles outcomes consistently.
How should I handle retries for transient errors?
Retry only on idempotent requests and transient failures (network errors, 5xx). Use exponential backoff and limit retries to avoid cascading load.
Where do I store the API base URL and token?
Keep the base URL in configuration/environment and inject it into providers. Store tokens in a secure storage service and inject a token getter into the authenticated client.