afv-library/skills/integration-connectivity-generate/assets/external-services/external-service-operations.md

5.5 KiB

External Service Operations Guide

Overview

External Services in Salesforce auto-generate Apex classes from OpenAPI specifications, providing type-safe API integrations without manual HTTP code.

Generated Class Structure

When you register an External Service named MyAPI, Salesforce generates:

ExternalService.MyAPI                          - Main service class
ExternalService.MyAPI_operationName_Request    - Request wrapper for each operation
ExternalService.MyAPI_operationName_Response   - Response wrapper for each operation
ExternalService.MyAPI_SchemaName               - DTO for each schema in OpenAPI

Basic Usage

1. Instantiate the Service

ExternalService.MyAPI api = new ExternalService.MyAPI();

2. Call GET Operation

// Simple GET with path parameter
ExternalService.MyAPI_getCustomer_Response response = api.getCustomer('cust_123');

// Access response data
String customerId = response.id;
String customerName = response.name;

3. Call POST Operation

// Create request object
ExternalService.MyAPI_createCustomer_Request request =
    new ExternalService.MyAPI_createCustomer_Request();
request.name = 'Acme Corp';
request.email = 'contact@acme.com';

// Make the call
ExternalService.MyAPI_createCustomer_Response response = api.createCustomer(request);

// Access created resource
String newCustomerId = response.id;

4. Call PUT/PATCH Operations

// Update request
ExternalService.MyAPI_updateCustomer_Request request =
    new ExternalService.MyAPI_updateCustomer_Request();
request.customerId = 'cust_123';  // Path parameter
request.name = 'Acme Corporation';  // Body field

ExternalService.MyAPI_updateCustomer_Response response = api.updateCustomer(request);

5. Call DELETE Operation

// DELETE usually returns void or simple confirmation
api.deleteCustomer('cust_123');

Error Handling

try {
    ExternalService.MyAPI api = new ExternalService.MyAPI();
    ExternalService.MyAPI_getCustomer_Response response = api.getCustomer('invalid_id');

} catch (ExternalService.ExternalServiceException e) {
    // API returned error status code
    System.debug('API Error: ' + e.getMessage());
    System.debug('Status Code: ' + e.getStatusCode());
    System.debug('Response Body: ' + e.getBody());

} catch (CalloutException e) {
    // Network/connection error
    System.debug('Callout failed: ' + e.getMessage());
}

Async Usage (Queueable)

External Service calls are still callouts, so use Queueable for trigger contexts:

public class CustomerSyncQueueable implements Queueable, Database.AllowsCallouts {

    private List<Account> accounts;

    public CustomerSyncQueueable(List<Account> accounts) {
        this.accounts = accounts;
    }

    public void execute(QueueableContext context) {
        ExternalService.MyAPI api = new ExternalService.MyAPI();

        for (Account acc : accounts) {
            try {
                ExternalService.MyAPI_createCustomer_Request req =
                    new ExternalService.MyAPI_createCustomer_Request();
                req.name = acc.Name;
                req.email = acc.Email__c;

                ExternalService.MyAPI_createCustomer_Response resp =
                    api.createCustomer(req);

                // Store external ID on Account
                acc.External_Customer_Id__c = resp.id;

            } catch (Exception e) {
                System.debug('Sync failed for ' + acc.Name + ': ' + e.getMessage());
            }
        }

        update accounts;
    }
}

OpenAPI Schema Tips

Required Properties

{
  "components": {
    "schemas": {
      "Customer": {
        "type": "object",
        "required": ["name", "email"],
        "properties": {
          "name": { "type": "string" },
          "email": { "type": "string", "format": "email" }
        }
      }
    }
  }
}

Nested Objects

{
  "Customer": {
    "type": "object",
    "properties": {
      "address": {
        "$ref": "#/components/schemas/Address"
      }
    }
  },
  "Address": {
    "type": "object",
    "properties": {
      "street": { "type": "string" },
      "city": { "type": "string" }
    }
  }
}

Arrays

{
  "CustomerList": {
    "type": "object",
    "properties": {
      "customers": {
        "type": "array",
        "items": {
          "$ref": "#/components/schemas/Customer"
        }
      }
    }
  }
}

Limitations

Limitation Workaround
100 callouts per transaction Use async (Queueable) with chaining
120s max timeout Use shorter timeout, implement retry
6MB response size Paginate responses, compress data
Some OpenAPI features not supported Simplify schema, avoid oneOf/anyOf

Refreshing External Service

When the API schema changes:

  1. Download updated OpenAPI spec
  2. Go to Setup → External Services
  3. Edit the service
  4. Upload new schema
  5. Review generated operations
  6. Save and validate

Or via metadata deployment:

  1. Update the <schema> content in the .externalServiceRegistration-meta.xml
  2. Deploy with sf project deploy start

Best Practices

  1. Version Your APIs: Include version in Named Credential endpoint
  2. Handle All Errors: Catch both ExternalServiceException and CalloutException
  3. Log Requests/Responses: For debugging production issues
  4. Use Async: Always use Queueable when called from DML contexts
  5. Test Thoroughly: Mock External Service calls in test classes