All posts
Field notes

Move 10 GB from Salesforce to Supabase, Save $30K and Keep the data visible

salesforcestorage-optimizationsupabaseappcolab-gridarchivingcost-reductionpostgresqlapex-batch
See how to move 5 million Survey_Item__c records to Supabase, reclaim 10 GB of Salesforce storage, save an estimated $30,000 a year, and keep every response visible inline with AppColab Grid.

What if the least valuable 10 GB in your Salesforce org is costing you $30,000 every year?

At a common planning rate of $250 per GB per month for additional Salesforce data storage, the math adds up fast:

10 GB × $250 per GB/month × 12 months = $30,000 per year

Your actual price will depend on your Salesforce edition, contract, and negotiated terms, but the budget pressure is the same: high-volume records that are rarely changed can become surprisingly expensive to keep on-platform.

If you manage a growing Salesforce org, you have probably seen this email:

Subject: Warning: Your organization has exceeded its data storage limit.

At first, you check your largest objects. You expect to see bloated Accounts, Opportunities, or Cases. But more often than not, the culprit is a granular child object: survey question responses, order line items, audit logs, email messages or tracking events.

In Salesforce, every single custom object record consumes 2 KB of data storage, whether it contains 50 rich text fields or just a single score. When a single survey submission creates 25 child rows, standard storage limits vanish within months.

Purchasing more storage solves the immediate limit, but at roughly $250 per GB per month it can turn a data-retention problem into a five-figure recurring bill.

Teams usually face a painful compromise:

  1. Hard delete old data: Reclaims storage, but permanently wipes out historical context and customer interaction history.
  2. Export to CSV / S3: Saves the data, but buries it in an external archive where sales and service reps can never see it without asking IT.
  3. Pay for expensive add-ons or Data Cloud: Adds massive licensing overhead for data that is mostly read-only.

In this post, we walk through a clean, cost-effective architectural pattern: keep master header records in Salesforce, stream high-volume child records to external PostgreSQL storage (Supabase), and render them inline directly on the Salesforce parent page using AppColab Grid.

To the end user, nothing changes - they open the parent record and see all child responses in real time. To Salesforce, the child storage footprint is 0 bytes.


The 5 Million-Row Opportunity: Customer Surveys

Consider a standard customer feedback architecture:

  • Parent Object (Survey__c): Represents the overall feedback interaction (Customer Contact, Completed Date, Status, Overall CSAT/NPS Score).
  • Child Object (Survey_Item__c): Represents individual answers to specific questions (Question text, Answer, Score, Category).

The Exponential Storage Math

A single completed survey with 25 questions produces:

  • 1 Survey header record
  • 25 Survey Item child records

Let’s scale that across a year of customer interactions. The savings estimate below uses $250 per GB per month and counts only the child records that can be archived:

Completed Surveys Header Records (Survey__c) Child Records (Survey_Item__c) Total Salesforce Storage Archivable Child Storage Estimated Annual Savings
10,000 10,000 250,000 ~520 MB ~500 MB ~$1,500
50,000 50,000 1,250,000 ~2.6 GB ~2.5 GB ~$7,500
200,000 200,000 5,000,000 ~10.4 GB ~10 GB ~$30,000

At 200,000 completed surveys, the 5 million child Survey_Item__c records alone consume about 10 GB. Archiving those rows while retaining the 200,000 lightweight Survey__c headers frees over 96% of the survey data footprint—and avoids an estimated $30,000 in annual storage spend at the planning rate above.

Furthermore, once a survey is submitted, those individual question responses are rarely modified. Agents only need to read them when reviewing customer sentiment or investigating an escalation.


The Architecture: Offload & View Inline

Rather than paying recurring storage fees for static child records, we move child records to Supabase, but still render them in survey record via AppColab Grid:

  1. Keep Survey__c in Salesforce: Reports, dashboards, and automated flows can still group by Status, Contact, or Overall Score.
  2. Batch Offload to Supabase: An automated Apex batch job extracts Survey_Item__c records, sends them in bulk to Supabase, and deletes the Salesforce records upon confirmed receipt.
  3. Render Offloaded data via AppColab Grid: An AppColab Grid component embedded on the Survey__c Lightning Record Page queries Supabase in real time using the current Survey’s Record ID (survey_id = :parentId).

Want help adapting this architecture to your Salesforce org? Email admin@appcolab.com or fill out our contact form, and the AppColab team will be happy to help.

Step-by-Step Implementation

Step 1: Create the Supabase PostgreSQL Schema

In your Supabase Dashboard, open the SQL Editor (>_ in the left sidebar) and execute the following DDL script:

-- 1. Create table to store archived Salesforce Survey Items
CREATE TABLE IF NOT EXISTS survey_items (
    id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
    survey_id TEXT NOT NULL,         -- Stores the Salesforce Survey__c Record ID (Parent link)
    salesforce_id TEXT,              -- Original Survey_Item__c Record ID for audit trail
    question TEXT NOT NULL,
    answer TEXT,
    score NUMERIC(5,2),
    category TEXT,
    created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 2. Create index on survey_id for sub-100ms parent lookups
CREATE INDEX IF NOT EXISTS idx_survey_items_survey_id ON survey_items (survey_id);

-- 3. Enable Row Level Security (RLS)
ALTER TABLE survey_items ENABLE ROW LEVEL SECURITY;

-- 4. Allow API access via your project's anon key
CREATE POLICY "Allow all access to survey_items"
ON survey_items
FOR ALL
USING (true)
WITH CHECK (true);

Supabase Table Editor

The index on survey_id is critical. It guarantees that queries from AppColab Grid filter millions of rows in milliseconds when loading a specific survey’s responses.


Step 2: Connect Salesforce to Supabase

Salesforce connects to Supabase securely via Named Credentials using Supabase’s REST API (/rest/v1/).

If you haven’t connected Supabase to your Salesforce org yet, follow our step-by-step setup guide: 👉 Read the AppColab Supabase Grid Integration Guide


Step 3: Run the Offload Batch (SurveyItemBackupBatch)

To safely transfer the records and delete them from Salesforce, we use a stateful Apex Batch class.

The batch processes child records in chunks of 200, sends them to Supabase via SupabaseClient.insertRecords(), and executes delete scope only after a successful HTTP 201/200 response.

Here is the core logic inside the batch:

public void execute(Database.BatchableContext bc, List<sObject> scope) {
    List<Map<String, Object>> recordsToInsert = new List<Map<String, Object>>();

    for (sObject record : scope) {
        Map<String, Object> payload = new Map<String, Object>();
        payload.put('salesforce_id', (String) record.get('Id'));
        payload.put('survey_id', (String) record.get('Survey__c'));
        payload.put('question', (String) record.get('Question__c'));
        payload.put('answer', (String) record.get('Answer__c'));
        payload.put('score', (Decimal) record.get('Score__c'));
        payload.put('category', (String) record.get('Category__c'));
        recordsToInsert.add(payload);
    }

    try {
        // 1. Bulk insert into Supabase PostgREST table
        SupabaseClient client = new SupabaseClient();
        client.insertRecords('survey_items', recordsToInsert);

        // 2. Reclaim Salesforce storage only after successful API confirmation
        delete scope;
    } catch (Exception ex) {
        System.debug(LoggingLevel.ERROR, 'Archival failed: ' + ex.getMessage());
        // Records remain safely in Salesforce if any callout fails
    }
}
Executing the Batch in Developer Console

To run the archival on demand:

  1. Open Developer Console in Salesforce.
  2. Go to DebugOpen Execute Anonymous Window.
  3. Paste and run:
    Database.executeBatch(new SurveyItemBackupBatch(), 200);
    
Scheduling Automatic Nightly Archival

You can schedule the batch to run automatically every night at 2:00 AM:

System.schedule(
    'Nightly Survey Item Archival',
    '0 0 2 * * ?',
    new SurveyItemBackupBatch()
);

Once the batch finishes, Survey_Item__c records are safely moved to Supabase.


Step 4: Configure AppColab Grid with Parent-Child Filtering

Now that the child data lives in Supabase, we configure AppColab Grid to display it inline on the Survey__c parent record.

Deploy SupabaseGridDataProvider class and related components from our extensions repo AppColab-Grid-Extensions.

  1. Navigate to the AppColab Grid app launcher → Grid DefinitionsNew.
  2. Select External Data Provider.
  3. Set Data Provider Class to SupabaseGridDataProvider.

AppColab Grid Configuration

Understanding the JSON Configuration: 2 vs 3 Parameters

When configuring the Data Provider Configuration (JSON), understand the difference between Live Record Pages and Wizard Previews:

1. Production / Record Page Configuration (2 Parameters)

On a live Lightning record page, you only need two parameters:

{
  "tableName": "survey_items",
  "parentMatchColumn": "survey_id"
}

Why? When AppColab Grid renders inside a Survey__c page, it automatically detects the current record’s ID (context.parentId) and appends survey_id=eq.<RecordId> to the Supabase query. It dynamically displays only the responses for whichever survey you are currently viewing!

2. Testing in the Setup Wizard Preview (3 Parameters - Optional)

Inside the Grid Definition creation wizard, you are not on a record page, so context.parentId is null. If you want to test and preview actual data inside the wizard preview step, you can supply a sample Survey ID or AutoNumber:

{
  "tableName": "survey_items",
  "parentMatchColumn": "survey_id",
  "surveyId": "SRV-0002"
}

SupabaseGridDataProvider automatically resolves "SRV-0002" to its underlying Salesforce record ID, allowing admins to verify columns and sample rows before publishing.


Step 5: Embed on the Survey Lightning Record Page

To give users a seamless experience:

  1. Open any Survey__c record in your org.
  2. Click the Setup Gear (⚙️)Edit Page.
  3. In the Lightning App Builder, add a new tab labeled “Survey Responses” (or “Archived Details”).
  4. Drag the AppColab Grid component into the tab.
  5. In the component properties panel on the right, select your Supabase Survey Items Grid Definition.
  6. Click Save and Activate.

AppColab Grid Live Preview

When users open any survey record link SRV-0000, SRV-0001, or SRV-0002, AppColab Grid instantly loads the related Survey_Item__c records from Supabase with full support for search, sorting, and CSV export.

Need support with the Supabase connection, archival batch, or AppColab Grid configuration? Email admin@appcolab.com or contact us online.


What Architects and Admins Need to Know

1. Security & Compliance

  • Credential Storage: Salesforce Named Credentials encrypt the Supabase API key at rest. Secrets are never exposed in client-side code.
  • Row Level Security (RLS): In production environments, Supabase RLS policies can be scoped to validate specific tenant tokens or signed JWTs if multi-tenancy is required.
  • Audit Traceability: Storing the original Salesforce ID in the salesforce_id column ensures complete auditability across both systems.

2. Cost Comparison: Salesforce vs. Supabase

Feature Native Salesforce Storage Supabase (PostgreSQL)
Included Tier 20 MB (Dev) / 10 GB (Enterprise) 500 MB Free (holds ~250,000+ rows)
Additional Storage ~$250 / GB / month (~$3,000 / GB / year) $0.125 / GB / month beyond the plan allowance
API Costs Governed by Salesforce API limits Unlimited REST API requests
Query Performance Fast within limits Sub-100ms via indexed PostgreSQL

At that planning rate, archiving 10 GB of historical detail records avoids an estimated $30,000 per year in Salesforce storage add-on spend. Supabase plan, compute, backup, and egress charges still apply, but the incremental database storage cost is dramatically lower.

3. Where Else Does This Pattern Apply?

This architecture works for any high-volume detail object where historical data must remain accessible:

  • Order Line Items: Archive lines once an order is marked Delivered or Closed.
  • Integration & Error Logs: Keep logs for audit compliance without filling up CRM storage.
  • Customer Service Task Histories & Transcripts: Retain chat transcripts and telephony logs.
  • Email Messages: Offload email messages from marketing systems like marketing cloud, hubspot etc.

Conclusion

Data storage limits shouldn’t force your team into an ultimatum between sky-high Salesforce bills and permanently deleting customer history.

By combining an external PostgreSQL database like Supabase with AppColab Grid, you get the best of both worlds:

  • Zero data storage consumed in Salesforce for high-volume child records.
  • Complete visibility for your users, rendered inline with rich searching, filtering, and export capabilities.

Ready to connect your external databases to Salesforce? Install AppColab Grid on Salesforce AppExchange and start building external data grids in minutes.

Field notes

Want more like this?

We publish field notes from real client engagements — Salesforce, AWS, AI and modern web. Browse the rest, or talk to a real human about the problem you're trying to ship.

Comments

  • No comments yet. Be the first to share your thoughts.

Sign in to join the conversation.