Cache
- Introduction
- Configuration
- Retrieving Items
- Storing Items
- The Remember Method
- Removing Items
- Custom Cache Drivers
Introduction
Magic provides an expressive, unified API for various caching backends. The framework ships with a native FileStore driver that supports both Mobile/Desktop (File System) and Web (LocalStorage).
Caching is essential for reducing API calls, storing computed values, and improving application performance.
Configuration
Enabling Cache Support
Add CacheServiceProvider to your providers in config/app.dart:
'providers': [
(app) => CacheServiceProvider(app),
// ... other providers
],
Cache Configuration
Create lib/config/cache.dart:
import 'package:magic/magic.dart';
Map get cacheConfig => {
'cache': {
'driver': FileStore(fileName: 'magic_cache'),
'ttl': 3600, // Default TTL in seconds
},
};
Retrieving Items
Use the Cache facade to retrieve items from the cache:
// Basic retrieval
final value = await Cache.get('key');
// With default value
final username = await Cache.get('username', defaultValue: 'Guest');
// Check existence
if (await Cache.has('user_settings')) {
final settings = await Cache.get('user_settings');
}
Storing Items
Store items with optional TTL (time-to-live):
// With custom TTL
await Cache.put('key', 'value', ttl: Duration(minutes: 10));
// With default TTL from config
await Cache.put('key', 'value');
// Store any serializable data
await Cache.put('user', {
'id': 1,
'name': 'John',
'email': '[email protected]',
});
The Remember Method
The most powerful caching pattern—retrieve from cache or compute and store:
final users = await Cache.remember(
'users',
Duration(minutes: 5),
() async {
return await Http.get('/users');
},
);
This method:
- Checks if
usersexists in cache - If yes, returns the cached value
- If no, calls the closure, stores the result, and returns it
API Response Caching
class UserController extends MagicController {
Future> getUsers() async {
return await Cache.remember('all_users', Duration(minutes: 10), () async {
final response = await Http.get('/users');
return (response.body as List).map((u) => User.fromMap(u)).toList();
});
}
}
Computed Values
final dashboardStats = await Cache.remember('dashboard_stats', Duration(hours: 1), () async {
return {
'total_users': await User.count(),
'active_monitors': await Monitor.where('is_active', true).count(),
'incidents_today': await Incident.whereDate('created_at', Carbon.today()).count(),
};
});
Removing Items
// Remove a single item
await Cache.forget('users');
// Clear entire cache
await Cache.flush();
Invalidating Related Cache
class UserController extends MagicController {
Future updateUser(Map data) async {
final response = await Http.put('/users/${data['id']}', data: data);
if (response.successful) {
// Invalidate related caches
await Cache.forget('all_users');
await Cache.forget('user_${data['id']}');
await Cache.forget('dashboard_stats');
}
}
}
Custom Cache Drivers
Implement CacheStore for custom backends:
class RedisStore implements CacheStore {
final RedisClient _client;
RedisStore(this._client);
@override
Future init() async {
await _client.connect();
}
@override
Future get(String key, {dynamic defaultValue}) async {
final value = await _client.get(key);
if (value == null) return defaultValue;
return jsonDecode(value);
}
@override
Future put(String key, dynamic value, {Duration? ttl}) async {
final encoded = jsonEncode(value);
if (ttl != null) {
await _client.setex(key, ttl.inSeconds, encoded);
} else {
await _client.set(key, encoded);
}
}
@override
Future has(String key) async {
return await _client.exists(key) > 0;
}
@override
Future forget(String key) async {
await _client.del(key);
}
@override
Future flush() async {
await _client.flushdb();
}
}
Use your custom driver in config:
'cache': {
'driver': RedisStore(RedisClient()),
},
[!TIP] Use caching for expensive API calls, complex computations, and data that doesn't change frequently.