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 qualifications must specify no more than one row for retrieval. Result size: 3