Providers

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.

Creating Providers∞ 

A provider is simply an object with methods:

export const myProvider = {
  doSomething() {
    return 'hello'
  },

  doSomethingAsync() {
    return fetch('/api/data').then((response) => response.json())
  }
}

Using the Provider Factory∞ 

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'
  }
})

Adding Providers to Your App∞ 

Providers are added to your application in your module definition:

import * as providers from './providers'

export default {
  state: {
    // Your state
  },
  providers
}

Accessing Context∞ 

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.

Provider Options∞ 

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
  }
)

Available Options∞ 

  • wrap (boolean): When false, disables debugger wrapping for this provider
  • ignoreDefinition (boolean): When true, skips verification of the provider definition

Function-Based Providers∞ 

You 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.

Debugger Integration∞ 

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.

Testing∞ 

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
  }
})

Using Providers in Actions∞ 

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.