dehydrate static method

Map<String, dynamic> dehydrate({
  1. bool shouldDehydrate(
    1. QueryCacheEntry entry
    )?,
  2. dynamic serialize(
    1. dynamic data,
    2. List key
    )?,
})

Serializes the current BloomData cache into a plain, JSON-encodable Map.

Extracts cache records, preserving structured query keys, data payloads, timestamps (QueryCacheEntry.updatedAt), QueryCacheEntry.staleTime, QueryCacheEntry.cacheTime, and QueryCacheEntry.isStale flags so freshness is preserved across process boundaries during SSR dehydration and client hydration.

Pass shouldDehydrate to filter which entries are included (e.g. to exclude private session queries or other user data). By default, all non-expired entries are included.

Supply serialize to convert custom domain objects into JSON-compatible values (such as Map<String, dynamic>). If serialize is omitted and an entry's data is not directly JSON-encodable (or does not provide a .toJson() method), an ArgumentError is thrown with details identifying the offending query key.

End-to-End SSR Example

// ── 1. Server-Side Rendering (SSR) ──────────────────────────────────
// Populate cache during server render
BloomData.setQueryData<User>(['user', 42], (_) => currentUser);

// Dehydrate cache state to embed in the HTML response
final dehydrated = BloomData.dehydrate(
  serialize: (data, key) => data is User ? data.toJson() : data,
);
final scriptTag = BloomData.dehydrateToScriptTag(state: dehydrated);

// ── 2. Client-Side Hydration ────────────────────────────────────────
// On the browser, parse embedded JSON and hydrate BloomData before mounting
BloomData.hydrate(
  dehydrated,
  deserialize: (json, key) => key.first == 'user' ? User.fromJson(json as Map<String, dynamic>) : json,
);

// A query constructed here finds the fresh cache entry and does NOT refetch:
final userQuery = query<User>(
  key: ['user', 42],
  fetch: () => httpClient.get('/api/user/42'),
);
assert(userQuery.status.value == QueryStatus.success);
assert(userQuery.isFetching.value == false);

See also:

Implementation

static Map<String, dynamic> dehydrate({
  bool Function(QueryCacheEntry<dynamic> entry)? shouldDehydrate,
  dynamic Function(dynamic data, List<dynamic> key)? serialize,
}) {
  final queries = <Map<String, dynamic>>[];

  for (final entry in _cache.values) {
    if (entry.isExpired) continue;
    if (shouldDehydrate != null && !shouldDehydrate(entry)) continue;

    final dynamic serializedData;
    if (serialize != null) {
      serializedData = serialize(entry.data, entry.key);
    } else {
      serializedData = entry.data;
    }

    // Verify that serializedData is JSON-encodable when no custom serialize function handles it.
    try {
      jsonEncode(serializedData);
    } catch (e) {
      throw ArgumentError(
        'Data for query key "${normalizeKey(entry.key)}" is not JSON-encodable: $e. '
        'Provide a custom `serialize` function to BloomData.dehydrate() to convert domain objects.',
      );
    }

    queries.add({
      'key': entry.key,
      'data': serializedData,
      'updatedAt': entry.updatedAt.toIso8601String(),
      'staleTimeMs': entry.staleTime.inMilliseconds,
      'cacheTimeMs': entry.cacheTime.inMilliseconds,
      'isStale': entry.isStale,
    });
  }

  return {'queries': queries};
}