0

We are looking to read single Metadata component's files in a quick manner because in our usecase, user would be waiting in screen while we fetch the metadata.

SOAP API readMetadata is intended for this purpose but unfortunately it doesn't support ApexClass/ApexTrigger.

Here are the current approaches and its cons.

  • SOAP API retrieve - This async call works well but is slow
  • Tooling API: retrieve - This needs id of a apex class/trigger. If we just have name, then we need to make separate query to get that record. Also this api doesn't return the -meta.xml file so you will have to build that file based on information returned, which can be tricky.
  • Query ApexClass: We can query without needing to know the id but again issue is getting the -meta.xml

Are you aware of any other APIs that we can use to quickly fetch metadata content for a single component?

1 Answer 1

1

You can use the following format to get any record without using an ID:

/services/data/vXX.X/*/sobjects/**EntityName**/**ExternalIdField**/**ExternalIdValue**

For example, you can use:

/services/data/v55.0/sobjects/ApexClass/Name/Demo

Or:

/services/data/v55.0/tooling/sobjects/ApexClass/Name/Demo

Notably, Name is an External ID on virtually all objects that have a Name field, including ApexClass and ApexTrigger.

The Tooling API returns a property called Metadata that has the exact same contents as the -meta.xml file, including the active status and package version settings.

Example:

/services/data/v54.0/tooling/sobjects/ApexClass/Name/Demo

Output:

{
  "attributes" : {
    "type" : "ApexClass",
    "url" : "/services/data/v54.0/tooling/sobjects/ApexClass/01p3i00000Bn9d2AAB"
  },
  "Id" : "01p3i00000Bn9d2AAB",
  "NamespacePrefix" : null,
  "Name" : "Demo",
  "ApiVersion" : 55.0,
  "Status" : "Active",
  "IsValid" : true,
  "BodyCrc" : 1.361333916E9,
  "Body" : "public class Demo {\n\n}",
  "LengthWithoutComments" : 22,
  "CreatedDate" : "2022-09-25T15:34:32.000+0000",
  "CreatedById" : "0053i000001Zzz4AAC",
  "LastModifiedDate" : "2022-09-25T15:34:32.000+0000",
  "LastModifiedById" : "0053i000001Zzz4AAC",
  "SystemModstamp" : "2022-09-25T15:34:32.000+0000",
  "ManageableState" : "unmanaged",
  "SymbolTable" : {
    "constructors" : [ ],
    "externalReferences" : [ ],
    "id" : "Demo",
    "innerClasses" : [ ],
    "interfaces" : [ ],
    "key" : "Demo",
    "methods" : [ ],
    "name" : "Demo",
    "namespace" : null,
    "parentClass" : "",
    "properties" : [ ],
    "tableDeclaration" : {
      "annotations" : [ ],
      "location" : {
        "column" : 14,
        "line" : 1
      },
      "modifiers" : [ "public" ],
      "name" : "Demo",
      "references" : [ ],
      "type" : "Demo"
    },
    "variables" : [ ]
  },
  "Metadata" : {
    "apiVersion" : 55.0,
    "packageVersions" : [ ],
    "status" : "Active",
    "urls" : null
  },
  "FullName" : "Demo"
}

The Metadata property shown here is literally a JSON-formatted version of the -meta.xml file. It contains everything you need. You also get the Id of the class so you can use it with the MetadataContainer and related features (in this case, an ApexClassMember).

Note that the Tooling API is built to support IDE-style development. It's used by the internal Developer Console that Salesforce provides, and there are other IDEs out there that use this API as well. Whenever you need metadata-level information for code in real-time, the Tooling API is almost always the correct solution.

You can also use a Tooling API query:

/services/data/v54.0/tooling/query?q=select+fields(standard),metadata+from+apexclass+where+name%3d'demo'

Which gives you output like this:

{
  "size" : 1,
  "totalSize" : 1,
  "done" : true,
  "queryLocator" : null,
  "entityTypeName" : "ApexClass",
  "records" : [ {
    "attributes" : {
      "type" : "ApexClass",
      "url" : "/services/data/v54.0/tooling/sobjects/ApexClass/01p3i00000Bn9d2AAB"
    },
    "Metadata" : {
      "apiVersion" : 55.0,
      "packageVersions" : [ ],
      "status" : "Active",
      "urls" : null
    },
    "Id" : "01p3i00000Bn9d2AAB",
    "NamespacePrefix" : null,
    "Name" : "Demo",
    "ApiVersion" : 55.0,
    "Status" : "Active",
    "IsValid" : true,
    "BodyCrc" : 1.361333916E9,
    "Body" : "public class Demo {\n\n}",
    "LengthWithoutComments" : 22,
    "CreatedDate" : "2022-09-25T15:34:32.000+0000",
    "CreatedById" : "0053i000001Zzz4AAC",
    "LastModifiedDate" : "2022-09-25T15:34:32.000+0000",
    "LastModifiedById" : "0053i000001Zzz4AAC",
    "SystemModstamp" : "2022-09-25T15:34:32.000+0000"
  } ]
}

Note that "Metadata" is a special field. It works only when you're retrieving one item at a time. If you try more than that, you get:

When retrieving results with Metadata or FullName fields, the query qualificatio​ns must specify no more than one row for retrieval. Result size: 3

2
  • Thank you, that confirms tooling api is the best choice for us to retrieve ApexClass/Trigger but could use SOAP Api readMetadata to read single metadata about any other components.
    – metasync
    Commented Sep 25, 2022 at 16:58
  • 1
    @metasync Many components are available in the Tooling REST API. readMetadata is a lot slower, as it has a different purpose.
    – sfdcfox
    Commented Sep 25, 2022 at 19:23

You must log in to answer this question.

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