> ## Documentation Index
> Fetch the complete documentation index at: https://docs.velt.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Reactions

> Self-host your reactions data while using Velt's components. Keep reaction storage on your infrastructure with minimal metadata stored on Velt servers.

<Warning>
  * This is currently only compatible with `setDocuments` method.
  * Ensure that the data providers are set prior to calling `identify` method.
  * The data provider methods must return the correct status code (e.g. 200 for success, 500 for errors) and success boolean in the response object. This ensures proper error handling and retries.
</Warning>

# Overview

Velt supports self-hosting your reactions and related data:

* Reactions can be stored on your own infrastructure, with only necessary identifiers on Velt servers.
* Velt Components automatically hydrate reaction data in the frontend by fetching from your configured data provider.
* This gives you full control over reaction data while maintaining all Velt collaboration features.
* This automatically also ensures that the in-app notifications content related to reactions is not stored on Velt servers. The content is generated using the reactions data in the frontend.

# How does it work?

When users add or remove reactions:

1. The SDK uses your configured [`ReactionAnnotationDataProvider`](/api-reference/sdk/models/data-models#reactionannotationdataprovider) to handle storage
2. Your data provider implements three key methods:
   * `get`: Fetches reactions from your database
   * `save`: Stores reactions and returns success/error
   * `delete`: Removes reactions from your database

**The process works as follows:**

When a reaction operation occurs:

1. The SDK first attempts to save/delete the reaction on your database
2. If successful:
   * The SDK updates Velt's servers with minimal metadata
   * The [`PartialReactionAnnotation`](/api-reference/sdk/models/data-models#partialreactionannotation) object is updated with the reaction details including emoji, user, and metadata
   * When the reaction is saved, this information is stored on your end
   * Velt servers only store necessary identifiers, not the actual reaction content
3. If the operation fails, no changes are made to Velt's servers and the operation is retried if you have configured retries.

You can configure retries, timeouts, etc. for the data provider.

# Implementation Approaches

You can implement reaction self-hosting using either of these approaches:

1. **Endpoint based**: Provide endpoint URLs and let the SDK handle HTTP requests
2. **Function based**: Implement `get`, `save`, and `delete` methods yourself

Both approaches are fully backward compatible and can be used together.

| Feature            | Function based                                                                               | Endpoint based                                                                    |
| ------------------ | -------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| **Best For**       | Complex setups requiring middleware logic, dynamic headers, or transformation before sending | Standard REST APIs where you just need to pass the request "as-is" to the backend |
| **Implementation** | You write the `fetch()` or `axios` code                                                      | You provide the `url` string and `headers` object                                 |
| **Flexibility**    | High                                                                                         | Medium                                                                            |
| **Speed**          | Medium                                                                                       | High                                                                              |

## Endpoint based DataProvider

Instead of implementing custom methods, you can configure endpoints directly and let the SDK handle HTTP requests.

### getConfig

Config-based endpoint for fetching reactions. The SDK automatically makes HTTP POST requests with the request body.

* Type: [`ResolverEndpointConfig`](/api-reference/sdk/models/data-models#resolverendpointconfig)
* Request body format: [`GetReactionResolverRequest`](/api-reference/sdk/models/data-models#getreactionresolverrequest)
* Response format: [`ResolverResponse<Record<string, PartialReactionAnnotation>>`](/api-reference/sdk/models/data-models#resolverresponse)

<Tabs>
  <Tab title="React / Next.js">
    ```jsx theme={null}
    const reactionResolverConfig = {
      getConfig: {
        url: 'https://your-backend.com/api/velt/reactions/get',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      }
    };

    const reactionDataProvider = {
      config: reactionResolverConfig
    };

    <VeltProvider
      apiKey='YOUR_API_KEY'
      dataProviders={{ reaction: reactionDataProvider }}
    >
    </VeltProvider>
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```js theme={null}
    const reactionResolverConfig = {
      getConfig: {
        url: 'https://your-backend.com/api/velt/reactions/get',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      }
    };

    const reactionDataProvider = {
      config: reactionResolverConfig
    };

    Velt.setDataProviders({ reaction: reactionDataProvider });
    ```
  </Tab>
</Tabs>

### saveConfig

Config-based endpoint for saving reactions. The SDK automatically makes HTTP POST requests with the request body.

* Type: [`ResolverEndpointConfig`](/api-reference/sdk/models/data-models#resolverendpointconfig)
* Request body format: [`SaveReactionResolverRequest`](/api-reference/sdk/models/data-models#savereactionresolverrequest)
* Response format: [`ResolverResponse<T>`](/api-reference/sdk/models/data-models#resolverresponse)

<Tabs>
  <Tab title="React / Next.js">
    ```jsx theme={null}
    const reactionResolverConfig = {
      saveConfig: {
        url: 'https://your-backend.com/api/velt/reactions/save',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      }
    };

    const reactionDataProvider = {
      config: reactionResolverConfig
    };

    <VeltProvider
      apiKey='YOUR_API_KEY'
      dataProviders={{ reaction: reactionDataProvider }}
    >
    </VeltProvider>
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```js theme={null}
    const reactionResolverConfig = {
      saveConfig: {
        url: 'https://your-backend.com/api/velt/reactions/save',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      }
    };

    const reactionDataProvider = {
      config: reactionResolverConfig
    };

    Velt.setDataProviders({ reaction: reactionDataProvider });
    ```
  </Tab>
</Tabs>

### deleteConfig

Config-based endpoint for deleting reactions. The SDK automatically makes HTTP POST requests with the request body.

* Type: [`ResolverEndpointConfig`](/api-reference/sdk/models/data-models#resolverendpointconfig)
* Request body format: [`DeleteReactionResolverRequest`](/api-reference/sdk/models/data-models#deletereactionresolverrequest)
* Response format: [`ResolverResponse<T>`](/api-reference/sdk/models/data-models#resolverresponse)

<Tabs>
  <Tab title="React / Next.js">
    ```jsx theme={null}
    const reactionResolverConfig = {
      deleteConfig: {
        url: 'https://your-backend.com/api/velt/reactions/delete',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      }
    };

    const reactionDataProvider = {
      config: reactionResolverConfig
    };

    <VeltProvider
      apiKey='YOUR_API_KEY'
      dataProviders={{ reaction: reactionDataProvider }}
    >
    </VeltProvider>
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```js theme={null}
    const reactionResolverConfig = {
      deleteConfig: {
        url: 'https://your-backend.com/api/velt/reactions/delete',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      }
    };

    const reactionDataProvider = {
      config: reactionResolverConfig
    };

    Velt.setDataProviders({ reaction: reactionDataProvider });
    ```
  </Tab>
</Tabs>

### Endpoint based Complete Example

<Tabs>
  <Tab title="React / Next.js">
    ```jsx theme={null}
    const reactionResolverConfig = {
      getConfig: {
        url: 'https://your-backend.com/api/velt/reactions/get',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      },
      saveConfig: {
        url: 'https://your-backend.com/api/velt/reactions/save',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      },
      deleteConfig: {
        url: 'https://your-backend.com/api/velt/reactions/delete',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      },
      resolveTimeout: 2000,
      getRetryConfig: { retryCount: 3, retryDelay: 2000 },
      saveRetryConfig: { retryCount: 3, retryDelay: 2000 },
      deleteRetryConfig: { retryCount: 3, retryDelay: 2000 },
      additionalFields: ['commentAnnotationId', 'type']
    };

    const reactionDataProvider = {
      config: reactionResolverConfig
    };

    <VeltProvider
      apiKey='YOUR_API_KEY'
      dataProviders={{ reaction: reactionDataProvider }}
    >
    </VeltProvider>
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```js theme={null}
    const reactionResolverConfig = {
      getConfig: {
        url: 'https://your-backend.com/api/velt/reactions/get',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      },
      saveConfig: {
        url: 'https://your-backend.com/api/velt/reactions/save',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      },
      deleteConfig: {
        url: 'https://your-backend.com/api/velt/reactions/delete',
        headers: { 'Authorization': 'Bearer YOUR_TOKEN' }
      },
      resolveTimeout: 2000,
      getRetryConfig: { retryCount: 3, retryDelay: 2000 },
      saveRetryConfig: { retryCount: 3, retryDelay: 2000 },
      deleteRetryConfig: { retryCount: 3, retryDelay: 2000 },
      additionalFields: ['commentAnnotationId', 'type']
    };

    const reactionDataProvider = {
      config: reactionResolverConfig
    };

    Velt.setDataProviders({ reaction: reactionDataProvider });
    ```
  </Tab>
</Tabs>

## Function based DataProvider

Implement custom methods to handle data operations yourself.

### get

Method to fetch reactions from your database. On error we will retry.

* Param: [`GetReactionResolverRequest`](/api-reference/sdk/models/data-models#getreactionresolverrequest)
* Return: [`Promise<ResolverResponse<Record<string, PartialReactionAnnotation>>>`](/api-reference/sdk/models/data-models#resolverresponse)

<Tabs>
  <Tab title="Frontend Example">
    <CodeGroup>
      ```jsx React / Next.js theme={null}
      const fetchReactionsFromDB = async (request) => {
        const response = await fetch('/api/velt/reactions/get', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(request)
        });
        return await response.json();
      };

      const reactionDataProvider = {
        get: fetchReactionsFromDB,
      };

      <VeltProvider
        apiKey='YOUR_API_KEY'
        dataProviders={{ reaction: reactionDataProvider }}
      >
      </VeltProvider>
      ```

      ```js Other Frameworks theme={null}
      const fetchReactionsFromDB = async (request) => {
        const response = await fetch('/api/velt/reactions/get', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(request)
        });
        return await response.json();
      };

      const reactionDataProvider = {
        get: fetchReactionsFromDB,
      };

      Velt.setDataProviders({ reaction: reactionDataProvider });
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Backend Endpoint Example (MongoDB)">
    ```javascript theme={null}
    // Build query from request
    const { reactionAnnotationIds, documentIds, organizationId } = req.body;
    const query = {};
    if (reactionAnnotationIds?.length) {
      query.annotationId = { $in: reactionAnnotationIds };
    }
    if (documentIds?.length) {
      query.documentId = { $in: documentIds };
    }
    if (organizationId) {
      query.organizationId = organizationId;
    }

    const annotations = await collection.find(query).toArray();

    // Convert to Record<annotationId, annotation>
    const result = {};
    for (const annotation of annotations) {
      result[annotation.annotationId] = annotation;
    }

    // Return response in required format
    res.json({ data: result, success: true, statusCode: 200 });
    ```
  </Tab>

  <Tab title="Backend Endpoint Example (PostgreSQL)">
    ```javascript theme={null}
    // Build parameterized query
    const { reactionAnnotationIds, documentIds, organizationId } = req.body;
    const conditions = [];
    const values = [];
    let paramIndex = 1;

    if (reactionAnnotationIds?.length) {
      conditions.push(`annotation_id = ANY($${paramIndex++})`);
      values.push(reactionAnnotationIds);
    }
    if (documentIds?.length) {
      conditions.push(`document_id = ANY($${paramIndex++})`);
      values.push(documentIds);
    }
    if (organizationId) {
      conditions.push(`organization_id = $${paramIndex++}`);
      values.push(organizationId);
    }

    const whereClause = conditions.length ? `WHERE ${conditions.join(' AND ')}` : '';
    const { rows } = await client.query(
      `SELECT annotation_id, data FROM reaction_annotations ${whereClause}`,
      values
    );

    // Convert to Record<annotationId, annotation>
    const result = {};
    for (const row of rows) {
      result[row.annotation_id] = row.data;
    }

    // Return response in required format
    res.json({ data: result, success: true, statusCode: 200 });
    ```
  </Tab>
</Tabs>

### save

Save reactions to your database. Return a success or error response. On error we will retry.

* Param: [`SaveReactionResolverRequest`](/api-reference/sdk/models/data-models#savereactionresolverrequest)
* Return: [`Promise<ResolverResponse<T>>`](/api-reference/sdk/models/data-models#resolverresponse)

<Tabs>
  <Tab title="Frontend Example">
    <CodeGroup>
      ```jsx React / Next.js theme={null}
      const saveReactionsToDB = async (request) => {
        const response = await fetch('/api/velt/reactions/save', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(request)
        });
        return await response.json();
      };

      const reactionDataProvider = {
        save: saveReactionsToDB,
      };

      <VeltProvider
        apiKey='YOUR_API_KEY'
        dataProviders={{ reaction: reactionDataProvider }}
      >
      </VeltProvider>
      ```

      ```js Other Frameworks theme={null}
      const saveReactionsToDB = async (request) => {
        const response = await fetch('/api/velt/reactions/save', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(request)
        });
        return await response.json();
      };

      const reactionDataProvider = {
        save: saveReactionsToDB,
      };

      Velt.setDataProviders({ reaction: reactionDataProvider });
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Backend Endpoint Example (MongoDB)">
    ```javascript theme={null}
    const { annotations, context } = req.body;

    // Bulk upsert annotations
    const operations = Object.entries(annotations).map(([id, annotation]) => ({
      updateOne: {
        filter: { annotationId: id },
        update: {
          $set: {
            ...annotation,
            annotationId: id,
            documentId: context?.documentId || annotation.documentId,
            organizationId: context?.organizationId || annotation.organizationId,
          }
        },
        upsert: true
      }
    }));

    if (operations.length > 0) {
      await collection.bulkWrite(operations);
    }

    // Return response in required format
    res.json({ success: true, statusCode: 200 });
    ```
  </Tab>

  <Tab title="Backend Endpoint Example (PostgreSQL)">
    ```javascript theme={null}
    const { annotations, context } = req.body;

    // Transaction-based upsert
    await client.query('BEGIN');

    for (const [id, annotation] of Object.entries(annotations)) {
      const data = { ...annotation, annotationId: id };

      await client.query(
        `INSERT INTO reaction_annotations (annotation_id, document_id, organization_id, data, updated_at)
         VALUES ($1, $2, $3, $4, NOW())
         ON CONFLICT (annotation_id)
         DO UPDATE SET data = EXCLUDED.data, updated_at = NOW()`,
        [id, annotation.documentId, annotation.organizationId, JSON.stringify(data)]
      );
    }

    await client.query('COMMIT');

    // Return response in required format
    res.json({ success: true, statusCode: 200 });
    ```
  </Tab>
</Tabs>

### delete

Delete reactions from your database. Return a success or error response. On error we will retry.

* Param: [`DeleteReactionResolverRequest`](/api-reference/sdk/models/data-models#deletereactionresolverrequest)
* Return: [`Promise<ResolverResponse<T>>`](/api-reference/sdk/models/data-models#resolverresponse)

<Tabs>
  <Tab title="Frontend Example">
    <CodeGroup>
      ```jsx React / Next.js theme={null}
      const deleteReactionsFromDB = async (request) => {
        const response = await fetch('/api/velt/reactions/delete', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(request)
        });
        return await response.json();
      };

      const reactionDataProvider = {
        delete: deleteReactionsFromDB,
      };

      <VeltProvider
        apiKey='YOUR_API_KEY'
        dataProviders={{ reaction: reactionDataProvider }}
      >
      </VeltProvider>
      ```

      ```js Other Frameworks theme={null}
      const deleteReactionsFromDB = async (request) => {
        const response = await fetch('/api/velt/reactions/delete', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(request)
        });
        return await response.json();
      };

      const reactionDataProvider = {
        delete: deleteReactionsFromDB,
      };

      Velt.setDataProviders({ reaction: reactionDataProvider });
      ```
    </CodeGroup>
  </Tab>

  <Tab title="Backend Endpoint Example (MongoDB)">
    ```javascript theme={null}
    const { annotationId } = req.body;

    await collection.deleteOne({ annotationId });

    // Return response in required format
    res.json({ success: true, statusCode: 200 });
    ```
  </Tab>

  <Tab title="Backend Endpoint Example (PostgreSQL)">
    ```javascript theme={null}
    const { annotationId } = req.body;

    await client.query(
      'DELETE FROM reaction_annotations WHERE annotation_id = $1',
      [annotationId]
    );

    // Return response in required format
    res.json({ success: true, statusCode: 200 });
    ```
  </Tab>
</Tabs>

### config

Configuration for the reaction data provider.

* Type: [`ResolverConfig`](/api-reference/sdk/models/data-models#resolverconfig). Relevant properties:
  * `resolveTimeout`: Timeout duration (in milliseconds) for resolver operations
  * `getRetryConfig`: [`RetryConfig`](/api-reference/sdk/models/data-models#retryconfig). Configure retry behavior for get operations.
  * `saveRetryConfig`: [`RetryConfig`](/api-reference/sdk/models/data-models#retryconfig). Configure retry behavior for save operations.
  * `deleteRetryConfig`: [`RetryConfig`](/api-reference/sdk/models/data-models#retryconfig). Configure retry behavior for delete operations.
  * `additionalFields`: `string[]`. Specify additional fields from the [`ReactionAnnotation`](/api-reference/sdk/models/data-models#reactionannotation) object to include in the resolver request payloads sent to your backend. By default, only core content fields are sent. Use this to request extra fields you need (e.g., `commentAnnotationId`, `type`).

```jsx theme={null}
const reactionResolverConfig = {
  resolveTimeout: 2000,
  getRetryConfig: { retryCount: 3, retryDelay: 2000 },
  saveRetryConfig: { retryCount: 3, retryDelay: 2000 },
  deleteRetryConfig: { retryCount: 3, retryDelay: 2000 },
  additionalFields: ['commentAnnotationId', 'type']
};
```

### Function based Complete Example

<Tabs>
  <Tab title="React / Next.js">
    ```jsx theme={null}
    const fetchReactionsFromDB = async (request) => {
      const response = await fetch('/api/velt/reactions/get', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(request)
      });
      return await response.json();
    };

    const saveReactionsToDB = async (request) => {
      const response = await fetch('/api/velt/reactions/save', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(request)
      });
      return await response.json();
    };

    const deleteReactionsFromDB = async (request) => {
      const response = await fetch('/api/velt/reactions/delete', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(request)
      });
      return await response.json();
    };

    const reactionResolverConfig = {
      resolveTimeout: 2000,
      getRetryConfig: { retryCount: 3, retryDelay: 2000 },
      saveRetryConfig: { retryCount: 3, retryDelay: 2000 },
      deleteRetryConfig: { retryCount: 3, retryDelay: 2000 },
      additionalFields: ['commentAnnotationId', 'type']
    };

    const reactionDataProvider = {
      get: fetchReactionsFromDB,
      save: saveReactionsToDB,
      delete: deleteReactionsFromDB,
      config: reactionResolverConfig
    };

    <VeltProvider
      apiKey='YOUR_API_KEY'
      dataProviders={{ reaction: reactionDataProvider }}
    >
    </VeltProvider>
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```js theme={null}
    const fetchReactionsFromDB = async (request) => {
      const response = await fetch('/api/velt/reactions/get', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(request)
      });
      return await response.json();
    };

    const saveReactionsToDB = async (request) => {
      const response = await fetch('/api/velt/reactions/save', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(request)
      });
      return await response.json();
    };

    const deleteReactionsFromDB = async (request) => {
      const response = await fetch('/api/velt/reactions/delete', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify(request)
      });
      return await response.json();
    };

    const reactionResolverConfig = {
      resolveTimeout: 2000,
      getRetryConfig: { retryCount: 3, retryDelay: 2000 },
      saveRetryConfig: { retryCount: 3, retryDelay: 2000 },
      deleteRetryConfig: { retryCount: 3, retryDelay: 2000 },
      additionalFields: ['commentAnnotationId', 'type']
    };

    const reactionDataProvider = {
      get: fetchReactionsFromDB,
      save: saveReactionsToDB,
      delete: deleteReactionsFromDB,
      config: reactionResolverConfig
    };

    Velt.setDataProviders({ reaction: reactionDataProvider });
    ```
  </Tab>
</Tabs>

# Sample Data

<Tabs>
  <Tab title="Reaction annotation stored on your database">
    ```json theme={null}
    {
        "annotationId": "REACTION_ANNOTATION_ID",
        "metadata": {
            "apiKey": "API_KEY",
            "documentId": "DOCUMENT_ID",
            "organizationId": "ORGANIZATION_ID"
        },
        "icon": "HEART_FACE"
    }
    ```

    The reaction annotation on your database stores the actual reaction icon/emoji content.
  </Tab>

  <Tab title="Stored on Velt servers (reaction collection)">
    ```json theme={null}
    {
        "annotationId": "REACTION_ANNOTATION_ID",
        "commentAnnotationId": "COMMENT_ANNOTATION_ID",
        "from": {
            "userId": "USER_ID"
        },
        "involvedUserIds": ["USER_ID"],
        "isReactionResolverUsed": true,
        "lastUpdated": 1768542354775,
        "metadata": {
            "apiKey": "API_KEY",
            "clientDocumentId": "DOCUMENT_ID",
            "clientOrganizationId": "ORGANIZATION_ID",
            "documentId": "INTERNAL_DOC_ID",
            "organizationId": "INTERNAL_ORG_ID"
        },
        "pageInfo": {
            "baseUrl": "https://your-app.com",
            "path": "/",
            "url": "https://your-app.com/"
        },
        "reactions": [
            {
                "from": {
                    "userId": "USER_ID"
                },
                "lastUpdated": 1768542354775
            }
        ],
        "type": "reaction"
    }
    ```

    Only reaction identifiers and metadata are stored on Velt servers. The actual reaction icon/emoji remains on your infrastructure.
  </Tab>

  <Tab title="Stored on Velt servers (comments collection)">
    ```json theme={null}
    {
        "annotationId": "COMMENT_ANNOTATION_ID",
        "comments": [
            {
                "commentId": 184639,
                "from": {
                    "userId": "USER_ID"
                },
                "isCommentResolverUsed": true,
                "isCommentTextAvailable": true,
                "reactionAnnotationIds": ["REACTION_ANNOTATION_ID"]
            }
        ],
        "metadata": {
            "apiKey": "API_KEY",
            "documentId": "DOCUMENT_ID",
            "organizationId": "ORGANIZATION_ID"
        }
    }
    ```

    The comment annotation on Velt servers keeps a reference to the reaction annotation IDs (`reactionAnnotationIds`), but the actual reaction content is stored on your infrastructure.
  </Tab>
</Tabs>

# Debugging

You can subscribe to `dataProvider` events to monitor and debug get, save, and delete operations. The event includes a `moduleName` field that identifies which module triggered the resolver call, helping you trace data provider requests.

You can also use the [Velt Chrome DevTools extension](https://chromewebstore.google.com/detail/velt-devtools/nfldoicbagllmegffdapcnohakpamlnl) to inspect and debug your Velt implementation.

Type: [`ReactionResolverModuleName`](/api-reference/sdk/models/data-models#reactionresolvermodulename)

<Tabs>
  <Tab title="React / Next.js">
    ```jsx theme={null}
    import { useVeltClient } from '@veltdev/react';

    const { client } = useVeltClient();

    useEffect(() => {
      if (!client) return;

      const subscription = client.on('dataProvider').subscribe((event) => {
        console.log('Data Provider Event:', event);
        console.log('Module Name:', event.moduleName);
      });

      return () => subscription?.unsubscribe();
    }, [client]);
    ```
  </Tab>

  <Tab title="Other Frameworks">
    ```javascript theme={null}
    const subscription = Velt.on('dataProvider').subscribe((event) => {
      console.log('Data Provider Event:', event);
      console.log('Module Name:', event.moduleName);
    });

    // Unsubscribe when done
    subscription?.unsubscribe();
    ```
  </Tab>
</Tabs>
