Skip to main content
  • This feature is currently in beta and is subject to change.
  • 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.

Overview

Velt supports self-hosting your comments file attachments data:
  • Attachments can be stored on your own infrastructure, with only necessary identifiers on Velt servers.
  • Velt Components automatically hydrate attachment data in the frontend by fetching from your configured data provider.
  • This gives you full control over attachment data while maintaining the file attachment features.

How does it work?

  • When attachments are uploaded or deleted, the SDK uses your configured AttachmentDataProvider to handle storage
  • The data provider implements save and delete methods to interact with your storage backend
  • Velt handles the data mapping and realtime synchronization while delegating persistence of actual files to your infrastructure
  • For write requests (save, delete), the operation is first performed on your storage backend and only if we get a success response, the SDK will perform the operation on the Velt server. If the operation fails on your storage backend, the SDK will not perform the operation on the Velt server.
  • You can configure retries, timeouts, etc. for the data provider.
Here are the methods that you need to implement on the data provider:

save

Save attachments to your storage backend. Return the url with a success or error response. On error we will retry.
  • Sample Request
  • Sample Response
  • Error Response
{
  "file": "File object or Blob",
  "metadata": {
    "apiKey": "your-api-key",
    "documentId": "doc-123",
    "organizationId": "your-org-id",
    "folderId": "folder-789"
  },
  "fileName": "screenshot.png",
  "fileType": "image/png"
}

delete

Delete attachments from your storage backend. Return a success or error response. On error we will retry.
  • Sample Request
  • Sample Response
  • Error Response
{
  "url": "https://your-storage.com/files/screenshot.png",
  "metadata": {
    "apiKey": "your-api-key",
    "documentId": "doc-123",
    "organizationId": "your-org-id",
    "folderId": "folder-789"
  }
}

config

Configuration for the attachment data provider.

AWS S3 Storage Implementation Example

Here’s how to implement the data provider methods with AWS S3:
  • Save Method
  • Delete Method
  • Complete Setup
import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';

const s3Client = new S3Client({ region: 'us-east-1' });

const saveAttachmentToS3 = async (request: SaveAttachmentResolverRequest) => {
  try {
    const { file, fileName, metadata } = request;

    // Generate unique file path
    const fileKey = `attachments/${metadata.organizationId}/${metadata.documentId}/${Date.now()}-${fileName}`;

    // Convert file to buffer
    const fileBuffer = await file.arrayBuffer();

    // Upload to S3
    const command = new PutObjectCommand({
      Bucket: 'your-bucket-name',
      Key: fileKey,
      Body: Buffer.from(fileBuffer),
      ContentType: request.fileType
    });

    await s3Client.send(command);

    // Construct public URL
    const url = `https://your-bucket-name.s3.amazonaws.com/${fileKey}`;

    return {
      success: true,
      statusCode: 200,
      data: { url }
    };
  } catch (error) {
    console.error('Error uploading attachment:', error);
    return {
      success: false,
      statusCode: 500,
      message: 'Failed to upload attachment to storage'
    };
  }
};

Example Implementation

  • React / Next.js
  • Other Frameworks
const saveAttachmentsToDB = async (request: SaveAttachmentResolverRequest) => {
    const result = await __saveAttachmentsToYourDB__(request)
      .then((response) => {
        return { success: true, statusCode: 200, data: { url: response.url } };
      })
      .catch((error) => {
        return { success: false, statusCode: 500 };
    });
    return result;
};

const deleteAttachmentsFromDB = async (request: DeleteAttachmentResolverRequest) => {
    const result = await __deleteAttachmentsFromYourDB__(request)
      .then((response) => {
        return { success: true, statusCode: 200 };
      })
      .catch((error) => {
        return { success: false, statusCode: 500 };
    });
    return result;
};

const attachmentResolverConfig: ResolverConfig = {
    resolveTimeout: 2000,
    saveRetryConfig: {
        retryCount: 3,
        retryDelay: 2000
    },
    deleteRetryConfig: {
        retryCount: 3,
        retryDelay: 2000
    }
};


const attachmentDataProvider: AttachmentDataProvider = {
    save: saveAttachmentsToDB,
    delete: deleteAttachmentsFromDB,
    config: attachmentResolverConfig
};

<VeltProvider
    apiKey='YOUR_API_KEY'
    dataProviders={{
        attachment: attachmentDataProvider
    }}
>
</VeltProvider>

Sample Data

  • Stored on your storage
  • Stored on Velt servers
{
  "fileUrl": "https://your-storage.com/attachments/org-123/doc-456/1234567890-screenshot.png",
  "fileName": "screenshot.png",
  "fileType": "image/png",
  "metadata": {
    "organizationId": "org-123",
    "documentId": "doc-456",
    "folderId": "folder-789",
    "uploadedAt": "2025-07-16T04:00:19.770Z"
  }
}