Skip to content

Commit 4cee8e0

Browse files
committed
first commit
0 parents  commit 4cee8e0

1 file changed

Lines changed: 340 additions & 0 deletions

File tree

README.md

Lines changed: 340 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,340 @@
1+
# Riderescue API SDK
2+
3+
Flutter/Dart SDK for the Riderescue API.
4+
5+
## Installation
6+
7+
Add to your `pubspec.yaml`:
8+
9+
```yaml
10+
dependencies:
11+
riderescue_api:
12+
git:
13+
url: https://github.com/riderescue/riderescue_api_sdk.git
14+
path: packages/dart
15+
ref: main
16+
```
17+
18+
## Usage
19+
20+
### Initialization
21+
22+
```dart
23+
import 'package:riderescue_api/riderescue_api.dart';
24+
25+
await Server.init(
26+
baseUrl: 'https://api.riderescue.com',
27+
authStrategy: BearerStrategy(tokenKey: 'access_token'),
28+
defaultCacheTtl: const Duration(minutes: 5),
29+
);
30+
```
31+
32+
### Making API Calls
33+
34+
```dart
35+
final api = AuthApi(Server.dio);
36+
37+
final result = await api.login(
38+
LoginBody(
39+
email: email,
40+
password: password,
41+
),
42+
);
43+
```
44+
45+
### Error Handling
46+
47+
```dart
48+
final apiError = ApiErrorResponse.fromJson(error.response?.data);
49+
50+
final emailError = apiError.firstFieldError(LoginErrorFields.email);
51+
final passwordError = apiError.firstFieldError(LoginErrorFields.password);
52+
```
53+
54+
## Features
55+
56+
- ✅ Dio-based HTTP client
57+
- ✅ Cookie management
58+
- ✅ Hive caching
59+
- ✅ Riverpod state management
60+
- ✅ Upload progress tracking
61+
- ✅ Multiple auth strategies (Bearer, API Key, None)
62+
- ✅ Auto-generated endpoints and DTOs
63+
64+
## Structure
65+
66+
- `lib/src/client/` - Manually owned runtime API client code
67+
- `lib/src/generated/` - Auto-generated endpoints, DTOs, and clients
68+
69+
## Development
70+
71+
To regenerate the generated code:
72+
73+
```bash
74+
cd sdk/generators
75+
pnpm generate:dart
76+
```
77+
# Dependencies to add to pubspec.yaml
78+
79+
```yaml
80+
dependencies:
81+
dio: ^5.7.0
82+
dio_cookie_manager: ^3.1.1
83+
cookie_jar: ^4.0.8
84+
hive_flutter: ^1.1.0
85+
flutter_riverpod: ^2.5.1
86+
path_provider: ^2.1.4
87+
88+
dev_dependencies:
89+
build_runner: ^2.4.13
90+
hive_generator: ^2.0.1
91+
```
92+
93+
## main.dart
94+
95+
```dart
96+
void main() async {
97+
WidgetsFlutterBinding.ensureInitialized();
98+
99+
await Server.init(
100+
baseUrl: 'https://api.riderescue.com',
101+
authStrategy: BearerStrategy(tokenKey: 'access_token'),
102+
defaultCacheTtl: Duration(minutes: 5),
103+
);
104+
105+
runApp(const ProviderScope(child: MyApp()));
106+
}
107+
```
108+
109+
## Screen usage examples
110+
111+
```dart
112+
// GET with cache
113+
final result = await ref.read(apiProvider.notifier).send(
114+
GetRequest(
115+
endpoint: V1.brands,
116+
version: ApiVersion.v1,
117+
fromJson: (j) => BrandsResponse.fromJson(j as Map),
118+
cache: true,
119+
cacheTtl: Duration(minutes: 10),
120+
),
121+
);
122+
result.when(
123+
success: (data, message, _) => showSnackbar(message),
124+
error: (error, message, _) => showSnackbar(message),
125+
);
126+
127+
// POST
128+
final result = await ref.read(apiProvider.notifier).send(
129+
PostRequest(
130+
endpoint: V1.login,
131+
version: ApiVersion.v1,
132+
body: {'email': email, 'password': password},
133+
fromJson: AuthSessionResponse.fromJson,
134+
noAuth: true,
135+
),
136+
);
137+
138+
// Force refresh — bypasses cache
139+
await ref.read(apiProvider.notifier).send(
140+
GetRequest(
141+
endpoint: V1.brands,
142+
version: ApiVersion.v1,
143+
fromJson: (j) => BrandsResponse.fromJson(j as Map),
144+
forceRefresh: true,
145+
),
146+
);
147+
148+
// Invalidate cache for a whole endpoint family
149+
await ApiCache.invalidateWhere('/v1/brands');
150+
151+
// Upload with progress bar
152+
ref.read(uploadProvider.notifier).upload(
153+
UploadRequest(
154+
endpoint: V1.upload,
155+
version: ApiVersion.v1,
156+
fromJson: (j) => UploadResponse.fromJson(j as Map),
157+
files: [UploadFile.fromPath(field: 'photo', path: filePath)],
158+
fields: {'vehicleId': id},
159+
),
160+
);
161+
162+
// In widget tree — watch progress
163+
final progress = ref.watch(uploadProgressStreamProvider);
164+
progress.when(
165+
data: (p) => LinearProgressIndicator(value: p.percent),
166+
loading: () => const SizedBox.shrink(),
167+
error: (_, __) => const SizedBox.shrink(),
168+
);
169+
170+
// Listen to auth events anywhere
171+
AuthEvents.onUnauthorized.listen((e) {
172+
// refresh token or logout
173+
});
174+
175+
// Cookie helpers
176+
final cookies = CookieManager();
177+
final hasToken = await cookies.has('refresh_token');
178+
await cookies.clearAll(); // on logout
179+
CookieEvents.onCookieSet('refresh_token').listen((_) {
180+
// cookie was just set by backend
181+
});
182+
183+
// Save token after login (app creator does this)
184+
await BearerStrategy.saveToken(token, key: 'access_token');
185+
186+
// Full logout
187+
await Server.logout(tokenKey: 'access_token');
188+
```
189+
190+
---
191+
192+
**Complete `lib/server/` plan**
193+
194+
**Init — once at app start**
195+
196+
```dart
197+
Server.init(
198+
baseUrl: 'https://api.riderescue.com',
199+
defaultVersion: ApiVersion.v1,
200+
authStrategy: BearerStrategy(tokenKey: 'access_token'),
201+
cacheTtl: Duration(minutes: 5),
202+
apiKey: null, // future
203+
);
204+
```
205+
206+
**Folder structure — final**
207+
208+
```
209+
lib/server/
210+
api/
211+
api_client.dart — Dio instance, version routing, interceptors
212+
api_request.dart — all request types including upload
213+
api_result.dart — ApiResult<T> with data + message + success
214+
api_provider.dart — single Riverpod notifier screens talk to
215+
api_cache.dart — L1 memory + L2 Hive, TTL, manual invalidation
216+
api_endpoints.dart — endpoint constants + deprecation markers
217+
api_versions.dart — version enum + deprecation mechanism
218+
auth/
219+
auth_strategy.dart — abstract + Bearer + Cookie + ApiKey(future)
220+
auth_config.dart — token key constant, api key placeholder
221+
cookies/
222+
cookie_manager.dart — CookieJar wrapper, get/set/clear/clearByName
223+
cookie_events.dart — streams: onCookiesChanged, onCookiesCleared, onCookieSet
224+
upload/
225+
upload_provider.dart — upload notifier with progress stream
226+
upload_progress.dart — UploadProgress model (sent, total, percent, isDone)
227+
models/ — already exists, auto-generated
228+
server.dart — barrel export + Server.init()
229+
```
230+
231+
**Request types summary**
232+
233+
```
234+
ApiRequest.get() — endpoint, version, query, cache config, fromJson
235+
ApiRequest.post() — endpoint, version, body, fromJson
236+
ApiRequest.put() — endpoint, version, body, fromJson
237+
ApiRequest.patch() — endpoint, version, body, fromJson
238+
ApiRequest.delete() — endpoint, version, body, fromJson
239+
ApiRequest.upload() — endpoint, version, fields, files, fromJson
240+
```
241+
242+
**Cache**
243+
244+
```
245+
L1 memory — always, instant
246+
L2 Hive — persistent, own named box: 'server_cache'
247+
TTL — app-level default constant, per-request override
248+
Invalidate — by exact key or pattern
249+
```
250+
251+
**Auth**
252+
253+
```
254+
BearerStrategy — memory first → Hive fallback, key is app constant
255+
CookieStrategy — Dio CookieJar fully automatic
256+
ApiKeyStrategy — future, compile-time constant, all requests, opt-out per request
257+
Screens — can only opt out via noAuth: true
258+
Token refresh — app responsibility, layer emits onUnauthorized stream
259+
```
260+
261+
**Cookie streams**
262+
263+
```
264+
onCookiesChanged — any add/update
265+
onCookiesCleared — full clear
266+
onCookieSet(name) — specific cookie set
267+
```
268+
269+
**Auth event streams**
270+
271+
```
272+
onUnauthorized — 401
273+
onForbidden — 403
274+
```
275+
276+
**Upload**
277+
278+
```
279+
Progress stream — UploadProgress(sent, total, percent, isDone)
280+
Cancel token — screen can cancel mid-upload
281+
Result — same ApiResult<T> as all other requests
282+
Content-Type — auto-set by request type
283+
Supports — file path, bytes, stream, mixed FormData
284+
```
285+
286+
**ApiResult<T>**
287+
288+
```dart
289+
ApiResult<T>
290+
├── success: bool
291+
├── message: String // raw server message for snackbars
292+
├── data: T? // parsed model
293+
├── statusCode: int
294+
└── error: String? // logged as warning, shown if needed
295+
```
296+
297+
**Screen usage will look like:**
298+
299+
```dart
300+
// GET with cache
301+
ref.read(apiProvider.notifier).send(
302+
GetRequest(
303+
endpoint: V1.brands,
304+
version: ApiVersion.v1,
305+
fromJson: BrandsResponse.fromJson,
306+
cache: true,
307+
cacheTtl: Duration(minutes: 10),
308+
),
309+
);
310+
311+
// POST
312+
ref.read(apiProvider.notifier).send(
313+
PostRequest(
314+
endpoint: V1.login,
315+
version: ApiVersion.v1,
316+
body: {'email': email, 'password': password},
317+
fromJson: AuthSessionResponse.fromJson,
318+
),
319+
);
320+
321+
// Upload with progress
322+
ref.read(uploadProvider.notifier).upload(
323+
UploadRequest(
324+
endpoint: V1.uploadPhoto,
325+
version: ApiVersion.v1,
326+
files: [UploadFile(field: 'photo', path: filePath)],
327+
fields: {'vehicleId': id},
328+
fromJson: UploadResponse.fromJson,
329+
),
330+
);
331+
332+
// Watch progress
333+
final progress = ref.watch(uploadProgressProvider);
334+
LinearProgressIndicator(value: progress.percent);
335+
```
336+
337+
---
338+
339+
**Are you happy with this full plan?** Once you confirm I'll write each file one at a time in order, complete and ready to copy in.
340+
# flutter_api_bridge

0 commit comments

Comments
 (0)