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:

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 return true if 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?) or list(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

Use Case
Class
Interface
Return Type

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