# HTTP Client - [Introduction](#introduction) - [Configuration](#configuration) - [Making Requests](#making-requests) - [GET Requests](#get-requests) - [POST Requests](#post-requests) - [PUT, PATCH & DELETE](#put-patch--delete) - [RESTful Resources](#restful-resources) - [Handling Responses](#handling-responses) - [Response Properties](#response-properties) - [Validation Errors](#validation-errors) - [File Uploads](#file-uploads) - [Interceptors](#interceptors) ## Introduction Magic provides a powerful HTTP client through the `Http` facade. Built on top of Dio, it offers a clean, expressive API for making HTTP requests and handling responses—just like you'd expect from Laravel. ## Configuration ### Network Config Create `lib/config/network.dart`: ```dart Map get networkConfig => { 'network': { 'default': 'api', 'drivers': { 'api': { 'base_url': env('API_BASE_URL', 'https://api.example.com/v1'), 'timeout': 10000, 'headers': { 'Accept': 'application/json', 'Content-Type': 'application/json', }, }, }, }, }; ``` ### Register in Config ```dart await Magic.init( configFactories: [ () => appConfig, () => networkConfig, ], ); ``` Don't forget to add `NetworkServiceProvider` to your app providers: ```dart 'providers': [ (app) => NetworkServiceProvider(app), // ... ], ``` ## Making Requests ### GET Requests ```dart // Simple GET final response = await Http.get('/users'); // With query parameters final response = await Http.get('/users', query: { 'page': 1, 'per_page': 25, 'sort': 'name', }); // Access the data if (response.successful) { final users = response.body; // Parsed JSON } ``` ### POST Requests ```dart final response = await Http.post('/users', data: { 'name': 'John Doe', 'email': 'john@example.com', 'password': 'secret123', }); if (response.successful) { final user = response.body; Magic.success('Success', 'User created!'); } ``` ### PUT, PATCH & DELETE ```dart // PUT - Full update await Http.put('/users/1', data: { 'name': 'Jane Doe', 'email': 'jane@example.com', }); // PATCH - Partial update await Http.patch('/users/1', data: { 'name': 'Jane Smith', }); // DELETE await Http.delete('/users/1'); ``` ## RESTful Resources For RESTful APIs, Magic provides resource helper methods: ```dart // GET /users final all = await Http.index('users'); // GET /users/1 final one = await Http.show('users', '1'); // POST /users final created = await Http.store('users', { 'name': 'New User', 'email': 'new@example.com', }); // PUT /users/1 final updated = await Http.update('users', '1', { 'name': 'Updated Name', }); // DELETE /users/1 await Http.destroy('users', '1'); ``` ## Handling Responses The `MagicResponse` object provides helpful properties and methods for handling API responses. ### Response Properties ```dart final response = await Http.get('/users'); // Status checks response.successful // true if 2xx status response.failed // true if 4xx or 5xx response.unauthorized // true if 401 response.forbidden // true if 403 response.notFound // true if 404 response.isValidationError // true if 422 // Access data response.statusCode // HTTP status code response.body // Parsed response body response['key'] // Direct access to body key response.dataAs() // Typed access ``` ### Validation Errors Magic handles Laravel-style 422 validation errors elegantly: ```dart final response = await Http.post('/register', data: formData); if (response.isValidationError) { // Get all errors as a Map final errors = response.errors; // {'email': ['Email already taken'], 'password': ['Too short']} // Get flat list of all error messages final allMessages = response.errorsList; // ['Email already taken', 'Too short'] // Get just the first error (useful for snackbars) final firstError = response.firstError; // 'Email already taken' // Get the main error message final message = response.errorMessage; // 'The given data was invalid.' } ``` ### Controller Integration Use `ValidatesRequests` mixin in your controller for automatic error handling: ```dart class AuthController extends MagicController with ValidatesRequests { Future register(Map data) async { clearErrors(); final response = await Http.post('/register', data: data); if (response.successful) { // Handle success } else { // Automatically populates controller errors from 422 response handleApiError(response, fallback: 'Registration failed'); } } } ``` ## File Uploads ### Using MagicFile (Recommended) ```dart // Pick and upload an image final image = await Pick.image(); if (image != null) { final response = await image.upload('/upload', fieldName: 'avatar'); if (response.successful) { final url = response['url']; } } // With additional form data final response = await image.upload( '/upload', fieldName: 'photo', data: {'title': 'Profile Photo', 'public': true}, ); ``` ### Using Http.upload() ```dart final file = await Pick.file(extensions: ['pdf', 'doc']); final response = await Http.upload( '/documents', data: {'title': 'My Document'}, files: {'document': file}, ); ``` ## Interceptors Create interceptors to modify requests or handle responses globally: ```dart class AuthInterceptor extends Interceptor { @override void onRequest(RequestOptions options, RequestInterceptorHandler handler) async { // Add auth token to every request final token = await Auth.getToken(); if (token != null) { options.headers['Authorization'] = 'Bearer $token'; } handler.next(options); } @override void onResponse(Response response, ResponseInterceptorHandler handler) { // Log or transform successful responses handler.next(response); } @override void onError(DioException err, ErrorInterceptorHandler handler) { // Handle errors globally if (err.response?.statusCode == 401) ``` Register interceptors in your `NetworkServiceProvider`: ```dart class NetworkServiceProvider extends ServiceProvider { @override void boot() { Http.addInterceptor(AuthInterceptor()); Http.addInterceptor(LoggingInterceptor()); } } ``` ### Driver Plugin Hook For SDK integrations that need direct Dio access (e.g., `sentry_dio` for performance tracing, certificate pinning), use `configureDriver()`: ```dart final driver = Magic.make('network'); driver.configureDriver((dio) { dio.addSentry(); // Full Sentry performance tracing }); ``` > [!NOTE] > `configureDriver()` is specific to `DioNetworkDriver`. Resolve using `Magic.make('network')` to access it.