Get the FREE Ultimate OpenClaw Setup Guide →

nylo-storage

Scanned
npx machina-cli add skill nylo-core/claude-code/nylo-storage --openclaw
Files (1)
SKILL.md
12.8 KB

Nylo Storage

Overview

Nylo v7 provides three storage mechanisms: NyStorage for secure persistent storage (backed by flutter_secure_storage), Backpack for fast in-memory synchronous access, and cache() for time-based caching. They can be combined for hybrid patterns where data is persisted and also available synchronously.

When to Use

  • Persisting data across app restarts (NyStorage)
  • Fast synchronous reads during runtime (Backpack)
  • Caching API responses or computed data with expiration (cache)
  • Storing collections/lists of items
  • Managing temporary session data (sessions)
  • Dual storage: persistent + in-memory access
  • When NOT to use: For authentication data specifically, use nylo-auth; for API response caching, see nylo-networking

Quick Reference

ActionCode
Save to storageawait NyStorage.save("key", value)
Read from storageawait NyStorage.read<String>("key")
Delete from storageawait NyStorage.delete("key")
Save JSONawait NyStorage.saveJson("key", map)
Read JSONawait NyStorage.readJson("key")
Save with TTLawait NyStorage.saveWithExpiry("key", val, ttl: Duration(hours: 1))
Save to BackpackbackpackSave("key", value)
Read from BackpackbackpackRead<String>("key")
Save to bothawait NyStorage.save("key", value, inBackpack: true)
Cache with expiryawait cache().saveRemember("key", 300, () async => data)
Cache foreverawait cache().saveForever("key", () async => data)
Read cacheawait cache().get<String>("key")
Clear cacheawait cache().clear("key")
Add to collectionawait NyStorage.addToCollection("key", item: value)
Read collectionawait NyStorage.readCollection<String>("key")

NyStorage (Persistent Storage)

Stores data securely on device using flutter_secure_storage under the hood.

Basic Operations

// Save
await NyStorage.save("coins", 100);
await NyStorage.save("username", "Anthony");

// Read with type
int? coins = await NyStorage.read<int>("coins");
String? username = await NyStorage.read<String>("username");

// Read with default value
String name = await NyStorage.read("name", defaultValue: "Guest") ?? "Guest";

// Delete
await NyStorage.delete("username");
await NyStorage.deleteMultiple(["key1", "key2", "key3"]);
await NyStorage.deleteAll();
await NyStorage.deleteAll(excludeKeys: ["auth_token"]);

JSON Storage

Map<String, dynamic> user = {
  "name": "Anthony",
  "email": "anthony@example.com",
  "preferences": {"theme": "dark"},
};
await NyStorage.saveJson("user_data", user);
Map<String, dynamic>? userData = await NyStorage.readJson("user_data");

TTL (Time-to-Live)

Values expire automatically after a duration:

await NyStorage.saveWithExpiry(
  "session_token",
  "abc123",
  ttl: Duration(hours: 1),
);

String? token = await NyStorage.readWithExpiry<String>("session_token");
Duration? remaining = await NyStorage.getTimeToLive("session_token");
int removed = await NyStorage.removeExpired();

Batch Operations

// Save multiple
await NyStorage.saveAll({
  "username": "Anthony",
  "score": 1500,
  "level": 10,
});

// Read multiple
Map<String, dynamic?> values = await NyStorage.readMultiple<dynamic>([
  "username", "score", "level",
]);

Collections

Store and manage lists under a single key:

// Add to collection
await NyStorage.addToCollection("favorites", item: "Product A");
await NyStorage.addToCollection("cart_ids", item: 123, allowDuplicates: false);

// Save entire collection
await NyStorage.saveCollection<int>("cart_ids", [1, 2, 3, 4, 5]);

// Read collection
List<String> favorites = await NyStorage.readCollection<String>("favorites");
bool isEmpty = await NyStorage.isCollectionEmpty("cart_ids");

// Update by index
await NyStorage.updateCollectionByIndex<int>(
  0,
  (item) => item + 10,
  key: "scores",
);

// Update by condition
await NyStorage.updateCollectionWhere<Map<String, dynamic>>(
  (item) => item['id'] == 5,
  key: "products",
  update: (item) {
    item['quantity'] = item['quantity'] + 1;
    return item;
  },
);

// Delete from collection
await NyStorage.deleteFromCollection<String>(0, key: "favorites");
await NyStorage.deleteValueFromCollection<int>("cart_ids", value: 123);
await NyStorage.deleteFromCollectionWhere<Map<String, dynamic>>(
  (item) => item['expired'] == true,
  key: "coupons",
);

Model Storage

Models extending Model have built-in storage methods:

class User extends Model {
  String? name;
  String? email;

  static StorageKey key = 'user';
  User() : super(key: key);

  User.fromJson(dynamic data) : super(key: key) {
    name = data['name'];
    email = data['email'];
  }

  @override
  Map<String, dynamic> toJson() => {"name": name, "email": email};
}

// Save model
User user = User();
user.name = "Anthony";
await user.save();
await user.save(inBackpack: true); // Also save to Backpack

// Read model
User? user = await NyStorage.read<User>(User.key);

// Save to collection
await user.saveToCollection();
List<User> users = await NyStorage.readCollection<User>(User.key);

Storage Keys Configuration

Define keys in lib/config/storage_keys.dart:

final class StorageKeysConfig {
  static StorageKey auth = 'SK_AUTH';
  static StorageKey coins = 'SK_COINS';
  static StorageKey themePreference = 'SK_THEME_PREFERENCE';

  static syncedOnBoot() => () async {
    return [
      coins.defaultValue(0),
      themePreference.defaultValue('light'),
    ];
  };
}

StorageKey Extension Methods

// Save/Read
await StorageKeysConfig.coins.save(100);
await StorageKeysConfig.coins.save(100, inBackpack: true);
int? coins = await StorageKeysConfig.coins.read<int>();
int? coins = await StorageKeysConfig.coins.fromStorage<int>(defaultValue: 0);

// JSON
await StorageKeysConfig.coins.saveJson({"gold": 50});
Map? data = await StorageKeysConfig.coins.readJson<Map>();

// Delete
await StorageKeysConfig.coins.deleteFromStorage();
await StorageKeysConfig.coins.flush();

// Backpack (synchronous)
int? coins = StorageKeysConfig.coins.fromBackpack<int>();

// Collections
await StorageKeysConfig.coins.addToCollection<int>(100);
List<int> allCoins = await StorageKeysConfig.coins.readCollection<int>();

Backpack (In-Memory Storage)

Backpack is a singleton in-memory store for fast synchronous access. Data is NOT persisted when the app closes.

Basic Operations

// Save
backpackSave("user_token", "abc123");
backpackSave("user", userObject);
Backpack.instance.save("settings", {"darkMode": true});

// Read (synchronous)
String? token = backpackRead<String>("user_token");
User? user = backpackRead<User>("user");
var settings = Backpack.instance.read("settings");

// Read with default
String name = Backpack.instance.read<String>("name", defaultValue: "Guest") ?? "Guest";

// Check existence
bool hasTheme = Backpack.instance.contains("theme");

// Delete
backpackDelete("user_token");
backpackDeleteAll();

Append to Lists

Backpack.instance.append("recent_searches", "Flutter");
Backpack.instance.append("recent_searches", "Dart");
Backpack.instance.append("recent_searches", "Nylo", limit: 10);

Backpack Sessions

Organize related data into named groups:

// Update session values
Backpack.instance.sessionUpdate("cart", "item_count", 3);
Backpack.instance.sessionUpdate("cart", "total", 29.99);

// Read session values
int? itemCount = Backpack.instance.sessionGet<int>("cart", "item_count");
double? total = Backpack.instance.sessionGet<double>("cart", "total");

// Get all session data
Map<String, dynamic>? cartData = Backpack.instance.sessionData("cart");

// Remove individual key from session
Backpack.instance.sessionRemove("cart", "item_count");

// Clear entire session
Backpack.instance.sessionFlush("cart");

Dual Storage Pattern

Save to both persistent storage and Backpack simultaneously:

// Save to both at once
await NyStorage.save("auth_token", "abc123", inBackpack: true);

// Read synchronously from Backpack (fast)
String? token = Backpack.instance.read("auth_token");

// Read asynchronously from storage (persistent)
String? token = await NyStorage.read("auth_token");

Practical Example

Future<void> handleLogin(String token) async {
  // Persist and cache in memory
  await NyStorage.save("auth_token", token, inBackpack: true);
}

class ApiService extends NyApiService {
  Future<User?> getProfile() async {
    return await network<User>(
      request: (request) => request.get("/profile"),
      bearerToken: backpackRead("auth_token"), // synchronous access
    );
  }
}

Sync Storage to Backpack

await NyStorage.syncToBackpack();
await NyStorage.syncToBackpack(overwrite: true);

Sessions (Named In-Memory Groups)

Sessions group temporary data without persistence:

// Set session data
session('checkout')
    .add('items', ['Product A', 'Product B'])
    .add('total', 99.99)
    .add('coupon', 'SAVE10');

// Or set all at once
session('checkout', {
  'items': ['Product A', 'Product B'],
  'total': 99.99,
});

// Read session data
List<String>? items = session('checkout').get<List<String>>('items');
double? total = session('checkout').get<double>('total');
Map<String, dynamic>? allData = session('checkout').data();

// Delete from session
session('checkout').delete('coupon');
session('checkout').clear();

// Persist session to storage
await session('checkout').syncToStorage();
await session('checkout').syncFromStorage();

Cache (Time-Based Caching)

Cache with Expiration

// Cache for 300 seconds (5 minutes)
// Callback only executes on cache miss
Map<String, dynamic>? profile = await cache().saveRemember("profile", 300, () async {
  return await api<UserApiService>((r) => r.getProfile());
});

Cache Forever

Map<String, dynamic>? config = await cache().saveForever("config", () async {
  return await api<ConfigApiService>((r) => r.getConfig());
});

Direct Cache Storage

await cache().put("session_token", "abc123", seconds: 3600);
await cache().put("device_id", "xyz789"); // No expiration

Read Cache

String? value = await cache().get<String>("my_key");
Map<String, dynamic>? data = await cache().get<Map<String, dynamic>>("user_data");

Returns null if expired or absent, and automatically removes expired items.

Cache Management

await cache().clear("my_key");          // Remove single item
await cache().flush();                   // Remove all cached items
bool exists = await cache().has("my_key");
List<String> keys = await cache().documents();
int sizeInBytes = await cache().size();

Platform Support

Cache is available on iOS, Android, macOS, Windows, and Linux. NOT available on Web (callbacks always execute).

if (cache().isAvailable) {
  // Use caching
}

Storage Exceptions

ExceptionMeaning
StorageExceptionBase exception with message/key
StorageSerializationExceptionObject serialization failed
StorageDeserializationExceptionData deserialization failed
StorageKeyNotFoundExceptionKey not found
StorageTimeoutExceptionOperation exceeded time limit
try {
  await NyStorage.read<User>("user");
} on StorageDeserializationException catch (e) {
  print("Failed to load user: ${e.message}");
  print("Expected type: ${e.expectedType}");
}

Common Mistakes

MistakeFix
Reading from Backpack after app restart without syncBackpack is in-memory only; call NyStorage.syncToBackpack() on boot or use inBackpack: true when saving
Using cache().saveRemember() on webCache is not available on web platform; check cache().isAvailable first
Forgetting to register Model decodersFor NyStorage.read<User>() to work, the model must have a fromJson constructor and be registered in decoders
Using NyStorage for frequent synchronous readsNyStorage is async; use Backpack for synchronous reads in hot paths like interceptors
Not handling null returns from NyStorage.read()All read methods return nullable types; always handle the null case
Storing large objects in BackpackBackpack lives in memory; store only what you need for quick access, not entire datasets
Using cache().get() without checking expiryget() automatically removes expired items and returns null; always handle the null case
Confusing cache() with NyApiService cachingcache() is the standalone cache system; cacheKey/cacheDuration in network() is API-level caching

Source

git clone https://github.com/nylo-core/claude-code/blob/main/skills/nylo-storage/SKILL.mdView on GitHub

Overview

Nylo v7 provides three storage mechanisms: NyStorage for secure persistent storage, Backpack for fast in-memory access, and cache() for time-based caching. They can be combined for hybrid patterns where data is both persisted and immediately available during runtime.

How This Skill Works

NyStorage uses flutter_secure_storage to securely persist data. Backpack keeps data in memory for synchronous reads, while cache() handles time-based expiration. You can mix these mechanisms and even write to both to enable a dual storage pattern for quick access plus persistence.

When to Use It

  • Persist data across app restarts using NyStorage
  • Enable fast synchronous reads during runtime with Backpack
  • Cache API responses or computed data with expiration using cache()
  • Store collections or lists under a single key
  • Implement dual storage patterns for data that's both persistent and quickly accessible

Quick Start

  1. Step 1: Decide which data goes to NyStorage, Backpack, or cache() and import the APIs
  2. Step 2: Do basic operations, e.g. await NyStorage.save("coins", 100) and int? coins = await NyStorage.read<int>("coins")
  3. Step 3: Enable dual storage or caching where appropriate, e.g. await NyStorage.save("user", user, inBackpack: true) or await cache().saveRemember("api", 300, () async => fetchData())

Best Practices

  • Avoid storing authentication data with NyStorage; use nylo-auth for that scenario
  • Carefully balance writes between NyStorage and Backpack to keep data in sync
  • Use saveWithExpiry for temporary or session data that should expire
  • Leverage batch operations (saveAll, readMultiple) to minimize I/O
  • Use readCollection and addToCollection to manage lists under a single key

Example Use Cases

  • Save user profile fields to NyStorage and read them on app startup
  • Cache API responses with TTL and read from cache(), falling back to network when needed
  • Maintain a 'favorites' collection with addToCollection and readCollection
  • Store session data in Backpack for quick access during a short user session
  • Use saveAll and readMultiple to persist and retrieve several keys in one operation

Frequently Asked Questions

Add this skill to your agents
Sponsor this space

Reach thousands of developers