API Client#
Overview#
The Api class provides a static HTTP client with built-in authentication, error handling, and JSON support.
import 'package:duxt/duxt.dart';
// Make a GET request
final posts = await Api.get('/posts');
// Make a POST request
final newPost = await Api.post('/posts', body: {
'title': 'Hello World',
});
Configuration#
Configure the API client once in your app initialization:
// lib/main.dart
import 'package:duxt/duxt.dart';
void main() {
Api.configure(
baseUrl: 'https://api.example.com',
);
// Optional: Add default headers
Api.configure(
baseUrl: 'https://api.example.com',
headers: {
'X-API-Version': '1.0',
'Accept-Language': 'en',
},
);
runApp(App());
}
Making Requests#
GET#
final data = await Api.get('/posts');
// With query parameters
final data = await Api.get('/posts', query: {
'page': '1',
'limit': '10',
'search': 'hello',
});
POST#
final data = await Api.post('/posts', body: {
'title': 'My Post',
'content': 'Post content here',
});
PUT#
await Api.put('/posts/123', body: {
'title': 'Updated Title',
'content': 'Updated content',
});
PATCH#
await Api.patch('/posts/123', body: {
'title': 'Only update title',
});
DELETE#
await Api.delete('/posts/123');
Authentication#
Set an authentication token that's automatically included in all requests:
Api.setAuth('your-jwt-token');
Api.clearAuth();
if (Api.isAuthenticated) {
// User is logged in
}
Example login flow:
class AuthApi {
static Future<User> login(String email, String password) async {
final data = await Api.post('/auth/login', body: {
'email': email,
'password': password,
});
final token = data['token'] as String;
Api.setAuth(token);
await TokenStorage.save(token);
return User.fromJson(data['user']);
}
static Future<void> logout() async {
await Api.post('/auth/logout');
Api.clearAuth();
await TokenStorage.clear();
}
}
Error Handling#
The API client throws ApiException on errors:
try {
final posts = await Api.get('/posts');
} on ApiException catch (e) {
print('Status: ${e.statusCode}');
print('Message: ${e.message}');
print('Body: ${e.body}');
if (e.statusCode == 401) {
router.push('/login');
} else if (e.statusCode == 404) {
// Not found
} else if (e.statusCode >= 500) {
// Server error
}
} catch (e) {
print('Network error: $e');
}
Module Pattern#
Organize API calls into module-specific classes:
// lib/posts/api.dart
import 'package:duxt/duxt.dart';
import 'model.dart';
class PostsApi {
static Future<List<Post>> getAll({int page = 1, int limit = 10}) async {
final data = await Api.get('/posts', query: {
'page': page.toString(),
'limit': limit.toString(),
});
return Post.fromList(data as List);
}
static Future<Post> getOne(String id) async {
final data = await Api.get('/posts/$id');
return Post.fromJson(data as Map<String, dynamic>);
}
static Future<Post> create({
required String title,
required String content,
}) async {
final data = await Api.post('/posts', body: {
'title': title,
'content': content,
});
return Post.fromJson(data as Map<String, dynamic>);
}
static Future<Post> update(String id, {String? title, String? content}) async {
final data = await Api.patch('/posts/$id', body: {
if (title != null) 'title': title,
if (content != null) 'content': content,
});
return Post.fromJson(data as Map<String, dynamic>);
}
static Future<void> delete(String id) async {
await Api.delete('/posts/$id');
}
}
Use it in your pages:
final posts = await PostsApi.getAll(page: 1);
final post = await PostsApi.getOne('123');
await PostsApi.create(title: 'New', content: 'Content');