conscious-sapphire
conscious-sapphire2mo ago

multiple dataprovider usage

Hello, In my project, which consists of multiple backend services, we have a separate NestJS project acting as our BFF (Backend for Frontend). Since each service behaves differently — such as returning different data structures or requiring different query parameters — we maintain separate data providers accordingly. At this point, I have a question regarding the best approach to structuring these data providers: should we organize them based on services (i.e., per backend service), or should we structure them based on resources/pages on the UI? There are cases where a single page interacts with multiple backend services, which leads to challenges such as the need for manual cache invalidation. So, my core question is: When working with multiple data providers, is it better to create new providers per resource or per backend service? Personally, I find service-based organizations more reasonable in terms of the maintainability and configurability of data providers. I would appreciate your insights on this matter.
8 Replies
kapa.ai
kapa.ai2mo ago
Hi @Damla. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- Your question about structuring multiple data providers—whether by backend service or by resource/page—is a common one when working with Refine in a microservices or BFF architecture. Based on the official documentation and community discussions, the recommended and most maintainable approach is to organize your data providers per backend service. Here’s why: - Service-based data providers align with Refine’s native support for multiple data providers. You can define each backend service as a separate data provider and assign them to resources as needed. This keeps your data provider logic focused and easier to maintain, especially when each service has its own API conventions, authentication, or data structures. For example:
<Refine
dataProvider={{
default: dataProvider(API_URL_1),
serviceB: dataProvider(API_URL_2),
serviceC: dataProvider(API_URL_3),
}}
resources={[
{ name: "posts" }, // uses default
{ name: "products", meta: { dataProviderName: "serviceB" } },
{ name: "users", meta: { dataProviderName: "serviceC" } },
]}
/>

<Refine
dataProvider={{
default: dataProvider(API_URL_1),
serviceB: dataProvider(API_URL_2),
serviceC: dataProvider(API_URL_3),
}}
resources={[
{ name: "posts" }, // uses default
{ name: "products", meta: { dataProviderName: "serviceB" } },
{ name: "users", meta: { dataProviderName: "serviceC" } },
]}
/>

Multiple Data Providers – Docs - Resource-to-provider mapping is flexible. You can assign a specific data provider to each resource using the meta.dataProviderName property in your resource config, or override it in your data hooks. This allows a single page or resource to interact with multiple backends if needed, without coupling your data provider logic to UI structure.
Multiple Data Providers – Usage - Maintainability and configurability. Service-based organization makes it easier to update, test, or swap out a backend integration without affecting unrelated resources. It also matches the way most teams structure their backend code and API clients. - Cache invalidation and cross-service interactions. While Refine provides hooks like useInvalidate for cache management, handling cache invalidation across services is inherently a challenge in any architecture. Service-based providers don’t make this harder; rather, they keep concerns separated, and you can still manually invalidate or refetch resources as needed using Refine’s hooks.
Cache Invalidation Example In summary:
It is generally best to organize your data providers per backend service, not per resource or page. This approach is directly supported by Refine, scales well, and keeps your codebase maintainable and clear. Assign resources to the appropriate provider using the meta.dataProviderName property, and use Refine’s hooks to manage data fetching and cache invalidation as needed. If you have a page that needs to interact with multiple services, simply use the appropriate data provider for each resource or operation within that page. This is a common and supported pattern in Refine. If you need more concrete examples or run into specific edge cases, let me know! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@319845019848540160> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
conscious-sapphire
conscious-sapphireOP2mo ago
Hello, @kapa.ai To help solidify this decision and address some internal team discussions, could you provide more detailed pros and cons of both approaches? Specifically: Service-Based Data Providers (Recommended Approach):
dataProvider={{
serviceA: serviceADataProvider(),
serviceB: serviceBDataProvider(),
serviceC: serviceCDataProvider(),
}}

// Usage in pages that need multiple services:
const { data: resourceA } = useList({
resource: “resourceA”,
dataProviderName: “serviceA”
});
const { mutate: actionB } = useCustomMutation({
dataProviderName: “serviceB”
});
dataProvider={{
serviceA: serviceADataProvider(),
serviceB: serviceBDataProvider(),
serviceC: serviceCDataProvider(),
}}

// Usage in pages that need multiple services:
const { data: resourceA } = useList({
resource: “resourceA”,
dataProviderName: “serviceA”
});
const { mutate: actionB } = useCustomMutation({
dataProviderName: “serviceB”
});
vs. Page/Resource-Based Data Providers (Alternative Approach):
dataProvider={{
pageAProvider: pageADataProvider(), // combines serviceA + serviceB in bff and provides one bff module
pageBProvider: pageBDataProvider(),
resourceXProvider: resourceXDataProvider(), // custom combinations
}}
dataProvider={{
pageAProvider: pageADataProvider(), // combines serviceA + serviceB in bff and provides one bff module
pageBProvider: pageBDataProvider(),
resourceXProvider: resourceXDataProvider(), // custom combinations
}}
Specific questions: 1. What are the main advantages of service-based organization beyond maintainability? 2. What are the potential drawbacks of service-based approach? 3. What problems typically arise with page/resource-based data providers in complex applications? 4. Are there any performance implications when using multiple data providers on a single page vs. a combined provider? 5. How should we handle scenarios where ResourceA from ServiceA needs data from ServiceB for display? Should this be handled in: - Custom hooks that coordinate multiple data providers? - Backend aggregation in the BFF layer? - Client-side data transformation? 6. Are there any Refine-specific features or patterns that work better with service-based organization? Understanding these trade-offs will help us make the strongest case for our architectural decisions
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Certainly! Here’s a detailed comparison of service-based vs. page/resource-based data provider organization in Refine, based strictly on the available knowledge sources. --- 1. Main Advantages of Service-Based Organization - Alignment with Refine’s Native Support:
Refine natively supports multiple data providers, allowing you to map each backend service to a separate provider and assign them to resources as needed. This is the documented and recommended approach for handling multiple backends, as shown in the official guides and examples Multiple Data Providers – Docs.
- Configurability and Maintainability:
Service-based providers keep logic focused and modular. Each provider can be updated, tested, or swapped independently, which is especially useful when services have different data structures or authentication requirements. This modularity is highlighted as a key benefit in the documentation and blog posts Backend API and Data. - Flexible Resource-to-Provider Mapping:
You can assign resources to providers using the meta.dataProviderName property, or override it in hooks, allowing a single page to interact with multiple services without coupling provider logic to UI structure Multiple Data Providers – Docs. --- 2. Potential Drawbacks of Service-Based Approach - Cross-Service Coordination:
When a page needs to aggregate data from multiple services, you may need to coordinate multiple hooks and handle cache invalidation or data merging manually. This is a general challenge with microservices, not specific to Refine, but it’s noted in community discussions as a complexity point Github Issue #1885. - Increased Boilerplate:
If many pages require data from multiple services, you may end up with more hook calls and more explicit provider selection in your components. --- 3. Problems with Page/Resource-Based Data Providers - Tight Coupling to UI Structure:
Organizing providers by page or resource can lead to tight coupling between backend logic and frontend structure. This makes it harder to reuse logic, refactor pages, or share resources across different parts of the app. - Reduced Reusability and Scalability:
If a resource is needed on multiple pages, you may duplicate provider logic or have to maintain multiple versions, increasing maintenance overhead. - Complexity in Provider Management:
As your app grows, managing a large number of page/resource-specific providers can become unwieldy, especially if backend services change or new combinations are needed. --- 4. Performance Implications - No Documented Performance Penalty for Multiple Providers:
The knowledge sources do not mention any inherent performance penalty for using multiple data providers on a single page. Refine’s architecture is designed to support this pattern, and data fetching is managed via React Query, which handles caching and state efficiently Data Fetching. - Backend Aggregation May Reduce Client Requests:
If you aggregate data in the BFF and expose a single endpoint, you may reduce the number of client requests, but this comes at the cost of flexibility and may increase backend complexity. --- 5. Handling Cross-Service Data Dependencies - Backend Aggregation (BFF):
If ResourceA from ServiceA needs data from ServiceB, aggregating in the BFF is a common solution. This keeps the frontend simple and reduces the need for client-side coordination. - Custom Hooks for Coordination:
Alternatively, you can use multiple hooks in the frontend to fetch data from different providers and combine them as needed. This is supported by Refine’s flexible hook system Multiple Data Providers – Docs. - Client-Side Data Transformation:
If the data is lightweight and the logic is simple, you can fetch from both providers and merge the data client-side. The best approach depends on your use case:
- For complex joins or heavy data, prefer backend aggregation.
- For simple lookups or when you want to keep the BFF thin, use frontend coordination. --- 6. Refine-Specific Features/Patterns Favoring Service-Based Organization - Resource-to-Provider Mapping:
Refine’s meta.dataProviderName property and hook options are designed for service-based provider assignment, making this approach seamless Multiple Data Providers – Docs. - Built-in Data Hooks:
All core data hooks (useList, useOne, etc.) accept a dataProviderName parameter, allowing you to easily fetch from different services within the same component useDataProvider. - Provider Modularity:
Service-based providers align with Refine’s pluggable provider architecture, which is a core design principle General Concepts. --- Summary Table | Approach | Pros | Cons | |-------------------------|----------------------------------------------------------------------|---------------------------------------------------------------------| | Service-Based | - Modular, maintainable<br>- Native Refine support<br>- Flexible | - Manual coordination for cross-service data<br>- More hook calls | | Page/Resource-Based | - Can reduce client requests if BFF aggregates<br>- Simple for small apps | - Tight UI-backend coupling<br>- Poor reusability<br>- Hard to scale | --- Conclusion:
Refine’s architecture and features are designed to work best with service-based data providers. This approach offers the most flexibility, maintainability, and aligns with Refine’s resource/provider mapping capabilities. Page/resource-based providers may be tempting for simple cases or heavy backend aggregation, but they introduce coupling and maintenance challenges as your app grows. If you need further examples or have a specific scenario, let me know! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@319845019848540160> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
conscious-sapphire
conscious-sapphireOP2mo ago
@kapa.ai Additional Context: We’re implementing a pure proxy BFF - our NestJS BFF forwards requests to backend services without business logic transformation, organized by UI pages:
@Module({})
export class PageAModule {
@Get(‘/page-a/resource-x’)
@ProxyToServiceA({ path: ‘/api/resource-x’ })
getResourceX() {}
}
@Module({})
export class PageAModule {
@Get(‘/page-a/resource-x’)
@ProxyToServiceA({ path: ‘/api/resource-x’ })
getResourceX() {}
}
Could you provide detailed pros and cons of both approaches? Service-Based Data Providers:
dataProvider={{
serviceA: serviceADataProvider(),
serviceB: serviceBDataProvider(),
}}
const { data } = useList({ dataProviderName: “serviceA” });
const { mutate } = useCustomMutation({ dataProviderName: “serviceB” });
dataProvider={{
serviceA: serviceADataProvider(),
serviceB: serviceBDataProvider(),
}}
const { data } = useList({ dataProviderName: “serviceA” });
const { mutate } = useCustomMutation({ dataProviderName: “serviceB” });
vs. Page-Based Data Providers:
dataProvider={{
pageAProvider: pageADataProvider(), // connects to page-A BFF endpoints
pageBProvider: pageBDataProvider(), // connects to page-B BFF endpoints
}}
dataProvider={{
pageAProvider: pageADataProvider(), // connects to page-A BFF endpoints
pageBProvider: pageBDataProvider(), // connects to page-B BFF endpoints
}}
Questions: 1. Main advantages of service-based organization beyond maintainability? 2. Potential drawbacks of service-based approach? 3. Problems with page-based data providers in complex applications? 4. Does our pure proxy BFF change the recommendation? Would page-based BFF modules with page-based data providers make more sense? 5. Performance implications of multiple vs. combined providers? 6. Cross-service data scenarios - handle via custom hooks, backend aggregation, or client transformation? 7. Refine-specific features that work better with service-based organization? 8. Cache invalidation - organize by service or page/resource? Understanding these trade-offs will help us make the strongest architectural case.
kapa.ai
kapa.ai2mo ago
Let me check the knowledge base for you again. One moment! :books: ---------------------- Certainly! Here’s a detailed, source-based analysis of service-based vs. page-based data provider organization in Refine, with a focus on your pure proxy BFF scenario. --- 1. Main Advantages of Service-Based Organization (Beyond Maintainability) - Alignment with Refine’s Native Model:
Refine is designed to support multiple data providers mapped to backend services. This allows you to assign resources to providers using the meta.dataProviderName property or in data hooks, making it easy to fetch from different services within the same page or component. This flexibility is a core feature of Refine’s architecture and is highlighted in the documentation and examples Multiple Data Providers – Docs. - Configurability and Modularity:
Each service-based provider encapsulates the logic for a single backend, making it easier to update, test, or swap out integrations independently. This modularity is especially valuable when services evolve separately or have different requirements General Concepts. - Resource Reusability:
Resources can be reused across pages without duplicating provider logic, supporting DRY principles and easier scaling as your app grows. --- 2. Potential Drawbacks of Service-Based Approach - Manual Coordination for Cross-Service Data:
When a page needs to aggregate data from multiple services, you must coordinate multiple hooks and handle data merging or cache invalidation yourself. This is a general challenge with microservices, not specific to Refine, but it’s noted in community discussions as a complexity point Github Issue #1885. - More Explicit Provider Selection:
Developers must be aware of which provider to use for each resource, which can increase boilerplate in components that interact with multiple services. --- 3. Problems with Page-Based Data Providers in Complex Applications - Tight Coupling to UI Structure:
Organizing providers by page ties backend logic to frontend structure, making it harder to reuse logic, refactor pages, or share resources across the app. This can lead to duplicated logic and increased maintenance as the app grows General Concepts. - Reduced Flexibility:
If a resource is needed on multiple pages, you may duplicate provider logic or have to maintain multiple versions, increasing maintenance overhead. - Scalability Issues:
As your app grows, managing a large number of page-specific providers can become unwieldy, especially if backend services change or new combinations are needed. --- 4. Does a Pure Proxy BFF Change the Recommendation? The knowledge sources do not explicitly address pure proxy BFFs. However, since your BFF is only forwarding requests and not aggregating or transforming data, the service-based approach remains preferable. This is because: - Refine’s architecture is built around mapping resources to backend services, not UI pages. - Page-based providers would tightly couple your frontend to the BFF’s page modules, reducing flexibility and reusability. - If your BFF ever needs to aggregate or transform data, you can still introduce backend aggregation as needed, but the frontend should remain organized by service for maintainability and scalability. --- 5. Performance Implications: Multiple vs. Combined Providers - No Documented Performance Penalty:
The knowledge sources do not mention any inherent performance penalty for using multiple data providers on a single page. Refine’s data fetching is managed via React Query, which handles caching and state efficiently Data Fetching. - Backend Aggregation May Reduce Client Requests:
If you aggregate data in the BFF and expose a single endpoint, you may reduce the number of client requests, but this comes at the cost of flexibility and may increase backend complexity. --- 6. Cross-Service Data Scenarios - Backend Aggregation (BFF):
If you need to join or aggregate data from multiple services, backend aggregation is the most robust solution. This keeps the frontend simple and reduces the need for client-side coordination. - Custom Hooks for Coordination:
Alternatively, you can use multiple hooks in the frontend to fetch data from different providers and combine them as needed. This is supported by Refine’s flexible hook system Multiple Data Providers – Docs. - Client-Side Data Transformation:
For lightweight or simple data dependencies, you can fetch from both providers and merge the data client-side. --- 7. Refine-Specific Features That Work Better with Service-Based Organization - Resource-to-Provider Mapping:
Refine’s meta.dataProviderName property and hook options are designed for service-based provider assignment, making this approach seamless Multiple Data Providers – Docs. - Built-in Data Hooks:
All core data hooks (useList, useOne, etc.) accept a dataProviderName parameter, allowing you to easily fetch from different services within the same component useDataProvider. - Provider Modularity:
Service-based providers align with Refine’s pluggable provider architecture, which is a core design principle General Concepts. --- 8. Cache Invalidation: Organize by Service or Page/Resource? - Service-Based Invalidation:
Since Refine’s data hooks and cache management (via React Query) are organized by resource and provider, cache invalidation is more straightforward when organized by service. You can invalidate or refetch data for a specific resource/provider combination as needed Data Fetching. - Page-Based Invalidation:
Page-based providers may complicate cache management, especially if resources are reused across pages or if the same data is fetched via different providers. --- Summary Table | Approach | Pros | Cons | |-------------------------|----------------------------------------------------------------------|---------------------------------------------------------------------| | Service-Based | - Modular, maintainable<br>- Native Refine support<br>- Flexible | - Manual coordination for cross-service data<br>- More hook calls | | Page-Based | - May reduce client requests if BFF aggregates<br>- Simple for small apps | - Tight UI-backend coupling<br>- Poor reusability<br>- Hard to scale | --- Conclusion:
Even with a pure proxy BFF, the service-based data provider approach is more aligned with Refine’s architecture, offers greater flexibility, and scales better as your application grows. Page-based providers may seem convenient for simple cases, but they introduce coupling and maintenance challenges that outweigh their benefits in complex applications. If you need further examples or have a specific scenario, let me know! ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2mo ago
<@319845019848540160> Kapa.ai is still learning and improving, please let me know how I did by reacting below.

Did you find this page helpful?