Providers
Providers are a key concept in the ReactiveModel system. They allow you to decouple data access logic from the reactive state itself. By implementing a simple interface, providers make it possible to integrate your models and collections with any kind of data source — from local storage and IndexedDB to SQL, NoSQL, REST APIs, or even file systems.
🎯 Purpose
The main goal of a provider is to externalize persistence logic so the reactive layer (items and collections) can remain agnostic of how data is fetched, stored, or updated.
This enables:
Clean separation of concerns
Easier testing and mocking
Plug-and-play integration with various backends or storage engines
Uniform handling of local and remote data sources
🔁 Shared Concept
The provider concept is used in both:
Item<T>— for single entity operationsCollection<T>— for group operations
While both rely on a provider, each one requires a different interface, suited to its purpose.
📦 Item Providers
Used in the Item<T> class to interact with a single entity. You must implement the IEntityProvider interface:
Interface
Key Methods
load(specs?): (Optional) Fetches a single item by ID or other criteria. Should return the item data directly (not wrapped in a response object).publish(data): (Optional) Saves or updates an item. Should return the updated item data directly.delete(specs?): (Optional) Deletes an item. Should returntrueif successful, or throw an error if it fails.
Example
Usage in Item
📚 Collection Providers
Used in the Collection<T> class to interact with a list of items. You must implement the ICollectionProvider interface:
Interface
Key Methods
load(specs?)orlist(specs?): (Required) Fetches a list of items. Should return either:An array of items directly:
Promise<ItemData[]>An object with pagination info:
Promise<{ items: ItemData[], next?: string, total?: number }>
publish(data): (Optional) Creates or updates items in bulk.remove(specs?): (Optional) Removes items from the collection.
Example
Usage in Collection
⚙️ Key Benefits
✅ Agnostic of backend or storage engine: Works with REST APIs, GraphQL, IndexedDB, SQL, etc.
✅ Fully compatible with remote APIs or local storage: Implement once, use anywhere
✅ Uniform interface: Same pattern for loading, saving, deleting
✅ Reusable and testable: Easy to mock for unit tests
✅ Scales from simple files to complex data layers: Start simple, evolve as needed
🧪 Implementation Best Practices
Error Handling
Provider methods should handle errors internally and throw meaningful errors:
Response Transformation
Providers should return only the data, not the API response wrapper:
Type Safety
Use TypeScript interfaces to ensure type safety:
🔮 Advanced Usage
Custom Provider Logic
You can add custom logic to your providers:
Batch Operations
For collections, you can implement batch operations:
📄 Summary
Single Entity
Item<T>
IEntityProvider
Promise<ItemData>
Collection
Collection<T>
ICollectionProvider
Promise<ItemData[]> or Promise<{ items, next, total }>
Providers handle the complexity of API interactions while providing a clean interface for your reactive models. They validate responses, handle errors, and return only the necessary data, keeping your models focused on managing state and reactivity.
Last updated