Providers expose functionality to your actions, typically for handling side effects. They are a core concept in Cerebral’s architecture that helps you manage interactions with external systems in a structured way.
A provider is simply an object with methods:
export const myProvider = {
doSomething() {
return 'hello'
},
doSomethingAsync() {
return fetch('/api/data').then((response) => response.json())
}
}
You can use the Provider
factory from Cerebral to create providers with additional options:
import { Provider } from 'cerebral'
export const myProvider = Provider({
doSomething() {
return 'hello'
}
})
Providers are added to your application in your module definition:
import * as providers from './providers'
export default {
state: {
// Your state
},
providers
}
Inside provider methods, you can access the full context using this.context
:
export const myProvider = {
triggerSequenceOnEvent(event, sequencePath) {
// Access other providers or utilities through context
const sequence = this.context.get(sequencePath)
window.addEventListener(event, () => {
sequence()
})
},
getDataForUser(userId) {
// Use another provider from within your provider
return this.context.http.get(`/api/users/${userId}`)
}
}
This approach keeps the API concise while allowing providers to work together.
When using the Provider factory, you can pass options to control its behavior:
export const myProvider = Provider(
{
// Provider methods
doSomething() {
/* ... */
}
},
{
// Options
wrap: false // Disable debugger wrapping for this provider
}
)
false
, disables debugger wrapping for this providertrue
, skips verification of the provider definitionYou can also define providers as functions that receive the context and return an object:
export const myDynamicProvider = (context) => {
// Create provider based on context
return {
doSomething() {
return context.state.get('someValue')
}
}
}
While this approach is more flexible, it prevents certain optimizations that are possible with object-based providers.
By default, all provider methods are automatically tracked by Cerebral’s debugger, showing method calls, arguments, and return values. This helps you understand how side effects flow through your application.
Providers are easy to mock in tests:
// Mock provider for testing
const mockHttpProvider = {
get: jest.fn().mockResolvedValue({ result: 'mocked data' }),
post: jest.fn().mockResolvedValue({ success: true })
}
const app = App(rootModule, {
providers: {
http: mockHttpProvider
}
})
export const api = {
getPosts() {
return fetch('https://jsonplaceholder.typicode.com/posts').then(
(response) => response.json()
)
},
getUser(id) {
return fetch(`https://jsonplaceholder.typicode.com/users/${id}`).then(
(response) => response.json()
)
}
}
Instead of creating a generic HTTP provider, we’ve built a specific API provider for JSONPlaceholder. This approach is recommended as it makes your application code more readable and focused.