REST API와 상호 작용하기 위한 명확한 추상화를 제공하는 플러터/다트 패키지입니다. 오류 관리 및 자동 리프레시 토큰 지원을 갖춘 JWT 인증과 같은 일반적인 문제를 처리하면서 HTTP 요청을 단순화합니다. 여러 플랫폼에서 API 통신을 간소화하고, 반복 코드를 줄이며 네트워크 작업의 신뢰성을 높입니다. 보안성과 일관된 백엔드 통합이 필요한 앱에 적합합니다.
사용 사례
인증된 REST API와의 통합
HTTP 오류를 우아하게 처리하기
JWT 토큰 갱신 자동화
확장 가능한 백엔드 통합 구축
API 요청 라이프사이클 관리
크로스 플랫폼 네트워크 호출 지원
주요 기능
내장된 JWT 및 리프레시 토큰 처리
포괄적인 예외 처리
크로스 플랫폼 호환성
간단하고 재사용 가능한 API 클라이언트 인터페이스
자동 요청 가로채기
사용자 정의 가능한 기본 URL 및 헤더
적합한 대상
REST 백엔드를 갖춘 플러터 앱
보안 API 액세스가 필요한 프로젝트
네트워크 보일러플레이트를 줄이려는 개발자
다중 플랫폼 애플리케이션을 개발하는 팀
JWT 기반 인증을 사용하는 앱
고려 사항
올바른 JWT 토큰 설정이 필요합니다
외부 http 패키지에 의존
사용자 정의 오류 매핑이 필요할 수 있습니다
REST 스타일 엔드포인트로 제한
비HTTP 프로토콜에는 적합하지 않습니다
인증 흐름은 서버 로직과 일치해야 합니다
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
Rest API Client
A production-ready HTTP client for Flutter applications that simplifies REST API communication with built-in support for JWT authentication, automatic token refresh, response caching, retry logic, and comprehensive error handling.
Features
JWT Authentication - Automatic token management with secure storage (Keychain/EncryptedSharedPreferences)
Token Refresh - Two strategies: response-and-retry or preemptive refresh before expiry
Response Caching - Intelligent caching with configurable expiration
Retry Logic - Exponential backoff for failed requests
// GET requestfinal result = await client.get('/users');
// GET with query parametersfinal result = await client.get('/users', queryParameters: {'page': 1, 'limit': 10});
// POST requestfinal result = await client.post('/users', data: {'name': 'John', 'email': 'john@example.com'});
// PUT requestfinal result = await client.put('/users/1', data: {'name': 'John Updated'});
// PATCH requestfinal result = await client.patch('/users/1', data: {'name': 'John Patched'});
// DELETE requestfinal result = await client.delete('/users/1');
// HEAD requestfinal result = await client.head('/users/1');
With Type Conversion
Use onSuccess to parse responses into typed objects:
final result = await client.get<User>(
'/users/1',
onSuccess: (data) => User.fromJson(data),
);
if (result.hasData) {
User user = result.data!;
print(user.name);
}
Per-Request Options
final result = await client.get(
'/users',
options: RestApiClientRequestOptions(
headers: {'X-Custom': 'value'},
contentType: 'application/json',
requiresAuth: false, // Skip authentication for this request
silentException: true, // Don't broadcast exceptions
),
);
Caching Strategies
Cache-First (Stale-While-Revalidate)
Returns cached data immediately, then fetches fresh data:
awaitfor (final result in client.getStreamed('/users')) {
if (result.hasData) {
// First emission: cached data (if available)// Second emission: fresh network dataupdateUI(result.data);
}
}
Cache-Only
Get data from cache only (no network request):
final result = await client.getCached('/users');
if (result.hasData) {
print('Cached data: ${result.data}');
}
Cache-Or-Network
Try cache first, fall back to network:
final result = await client.getCachedOrNetwork('/users');
Custom Cache Duration
final result = await client.get(
'/users',
cacheLifetimeDuration: Duration(hours: 1),
);
Authentication
Login Flow
// 1. Make login requestfinal loginResult = await client.post('/auth/login', data: {
'email': 'user@example.com',
'password': 'password123',
});
// 2. Authorize the client with received tokensif (loginResult.hasData) {
await client.authorize(
jwt: loginResult.data['accessToken'],
refreshToken: loginResult.data['refreshToken'],
);
}
// 3. All subsequent requests will include the JWTfinal userResult = await client.get('/users/me');
Check Authorization Status
final isLoggedIn = await client.isAuthorized();
Logout
await client.unAuthorize();
await client.clearStorage(); // Clear tokens and cached data
Error Handling
Result Object
All requests return a Result<T> object:
final result = await client.get('/users');
// Check for dataif (result.hasData) {
print(result.data);
}
// Check for errorsif (result.isError) {
print(result.exception);
}
// Check specific error typesif (result.isConnectionError) {
print('No internet connection');
}
// Access HTTP statusprint(result.statusCode); // e.g., 200, 404, 500print(result.statusMessage); // e.g., "OK", "Not Found"
Exception Types
Exception
HTTP Status
Description
ValidationException
400, 404, 422
Input validation errors
UnauthorizedException
401
Authentication required
ForbiddenException
403
Access denied
ServerErrorException
500, 502
Server-side errors
NetworkErrorException
-
Connection issues
Global Exception Stream
Listen to all exceptions across the app:
client.exceptionHandler.exceptions.stream.listen((exception) {
if (exception isUnauthorizedException) {
// Redirect to login
navigatorKey.currentState?.pushReplacementNamed('/login');
} elseif (exception isNetworkErrorException) {
// Show offline bannershowSnackBar('No internet connection');
}
});
Suppress Exceptions
// Suppress for a single requestfinal result = await client.get(
'/users',
options: RestApiClientRequestOptions(silentException: true),
);
File Downloads
final result = await client.download(
'/files/document.pdf',
'/path/to/save/document.pdf',
onReceiveProgress: (received, total) {
final progress = (received / total * 100).toStringAsFixed(0);
print('Download progress: $progress%');
},
);
Headers
// Set content type
client.setContentType('application/json');
// Set language
client.setAcceptLanguageHeader('en');
// Add custom header
client.addOrUpdateHeader(key: 'X-Api-Key', value: 'your-api-key');
// Access current headersprint(client.headers);