My aim is to have sample data automatically imported into any scratch org I make.

The problem I have is that one of the records that I insert using a plan has a trigger action that creates another record. Other records inserted later in the plan need to reference the record created in the trigger. But because it wasn't created in the plan I don't have a reference to it that I can use.

So to simplify I want to insert a plan with 3 records: A, B and C. B is automatically created when A is inserted. C has a lookup to B but I cant reference it.

  • Using custom meta-data you can import sample data in the scratch org.
    – RCS
    Apr 4, 2018 at 5:30
  • @RCS could you provide a bit more information or a link? I'm not sure what you mean by custom meta-data
    – Aequitas
    Apr 4, 2018 at 5:32

2 Answers 2


I would recommend not relying on your trigger code to create the records. Instead, create the objects/fields by deploying a base package/artifact, then importing your data, and finally importing/activating your triggers/processes/etc. Doing this will allow you to fully script the creation of your new scratch orgs. If you absolutely insist on doing it the way you're doing, you're going to need to write some support scripts, which means it'll look something like this:

sfdx force:data:tree:import ...
sfdx force:data:tree:export ...
sfdx force:data:tree:import ...

The trick here would be myCustomParsingScript.sh, which would call more code you'd write to parse the results and create new files for import. If I were going to attempt to do this, I'd write the code in Node.JS, which basically means you'd write some JavaScript to read the files, parse the JSON, create new data files based on that input, then dump out the data in to other file(s) that would then be uploaded by further force:data:tree:import commands.

If this looks complicated and rather hard to maintain... well, it is. I've seen it in action before in an org I worked with; they had a custom Java application that was thousands of lines of code that had to be run every single time we spun up a new sandbox, and if the load process changed at all, we would have to tweak the Java code to import new objects/fields/etc. The records themselves were loaded from a CSV, but it was still a very fragile process.

By not relying on the records from the trigger, you'll reduce deployment times, required framework code you need to write, and have more predictable data.

  • Thanks, but both these methods seem overly difficult. It's a shame there's no simple solution to this after I spent so long setting up the sample data. (The trigger created object is new) I think I'll probably just try creating all the data directly from apex code as that seems simpler
    – Aequitas
    Apr 4, 2018 at 13:53

There's probably no work around that won't incur some degree of complexity. But since you need to rely on an Apex trigger, and Salesforce DX gives you the ability to execute anonymous Apex code, maybe you could use Apex and execute anonymous to populate the data instead.

This is a concept that's been knocking about in my head for some time without a lot of time to explore it, but you've instigated a little bit of checking on my part.

I've built a little POC to test out this idea in the context of your specific problem. Namely:

  • You need to be able to automatically create scratch orgs
  • Data needs to be populated through three levels of related objects
  • The "middle" object records are created via a trigger on insert of the top level object records
  • The lowest level object records are added as related to the middle level records

So let's say I have the following objects:

Fields: Id, Name

Fields: Id, Name, test_Parent__c (related to test_Parent__c object)

Fields: Id, Name, test_Child__c (related to test_Child__c object)

Now I have a basic trigger in place that, upon insert, adds records into test_Child__c that will be related to records in test_Parent__c.

trigger TestParentTrigger on test_Parent__c (after insert) {

  List<test_Child__c> insertList = new List<test_Child__c>();

  for (test_Parent__c rec: Trigger.new){
    insertList.add(new test_Child__c(Name='Child of ' + rec.Name, test_Parent__c=rec.Id));

  insert insertList;


Final ingredient is I add a text file into my DX project that I will execute using sfdx force:apex:execute

This looks like this:

List<test_Parent__c> parents = new List<test_Parent__c>();
parents.add(new test_Parent__c(name='Parent1'));
parents.add(new test_Parent__c(name='Parent2'));
insert parents;

//here trigger will populate child records

List<test_Parent__c> parentsWithChildren = [SELECT Id,Name, (SELECT Id,Name FROM test_Children__r) FROM test_Parent__c];

List<test_Grandchild__c> grandChildren = new List<test_Grandchild__c>();

for (test_Parent__c parent: parentsWithChildren){

  for (test_Child__c child: parent.test_Children__r){

    grandChildren.add(new test_Grandchild__c(Name='Grandchild of ' + parent.Name + ' Child of ' + child.Name, test_Child__c = child.Id));



insert grandChildren; 

I called this file loaddata.apxs. So I'd need to run it like this:

> sfdx force:apex:execute -f loaddata.apxs --loglevel debug

I can then test the successful execution:

> sfdx force:data:soql:query  -q 'SELECT Id,Name,test_Child__r.Name,test_Child__r.test_Parent__r.Name FROM test_Grandchild__c'

ID                  NAME                                             TEST_CHILD__R.NAME  TEST_CHILD__R.TEST_PARENT__R.NAME
──────────────────  ───────────────────────────────────────────────  ──────────────────  ─────────────────────────────────
a023E000002kZsuQAE  Grandchild of Parent1 Child of Child of Parent1  Child of Parent1    Parent1
a023E000002kZsvQAE  Grandchild of Parent2 Child of Child of Parent2  Child of Parent2    Parent2

Granted all of this is a super simplified, lowest-common-denomenator example. A real org could (and most likely would) have a good deal more complexity. But at the very least it demonstrates how you could use Apex itself as the scripting language to populate data in some instances. Plus there are so many ways it is limited including all the limits for execute anonymous, plus you can't pass in parameters or make anything dynamic.

Incidentally, if you'd like to play with the POC I threw together feel free to check it out in this github repo.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .