Connector

What is a Connector? #

A connector is a specialized node for calling external APIs and services. Connectors extend the base node functionality with built-in support for HTTP requests, authentication, retry policies, and rate limiting. They are the bridge between your Orchesty workflows and remote systems.

Key characteristics:

  • API-focused: Built specifically for calling external services
  • HTTP abstractions: Easy request building and response handling
  • Authentication support: Integrated with Applications for credentials
  • Rate limiting: Automatic request throttling via Limiter
  • Retry policies: Built-in retry logic for failed requests
  • Reusable: Use across multiple workflows

When to Use Connectors #

Use connectors when you need to communicate with external services:

  • Fetch data from external APIs (e.g., "Get contacts from CRM")
  • Send data to external services (e.g., "Create order in Shopify")
  • Invoke webhooks (e.g., "Send notification to Slack")
  • Upload/download files to/from cloud storage
  • Make authenticated API calls using OAuth2 or API keys

Connectors vs Custom Nodes #

Both connectors and custom nodes are nodes that extend the same base classes and are registered the same way. The difference is in their purpose:

AspectConnectorCustom Node
PurposeCall external APIs/servicesTransform, filter, route data
HTTP supportBuilt-in via getSender()None (you can add manually)
AuthenticationIntegrated with ApplicationsNone (pure data processing)
Rate limitingAutomatic via LimiterNot applicable
Retry policyBuilt-in for API failuresManual if needed
Base classAConnectorACommonNode
Use forAPI calls, external servicesData transformation, business logic

Use connectors for external communication. Use custom nodes for internal data processing.

See Custom Node for data transformation and routing.

Architecture #

The Connector Ecosystem #

graph TB
    Topology[Topology in Orchesty]
    Router[ConnectorRouter]
    Connector[Your Connector]
    Application[Application optional]
    ProcessDto[ProcessDto]
    RequestDto[RequestDto]
    ExternalAPI[External API]
    
    Topology -->|HTTP POST| Router
    Router -->|Creates| ProcessDto
    Router -->|Calls processAction| Connector
    Connector -->|Reads/writes| ProcessDto
    Connector -->|Gets credentials from| Application
    Connector -->|Creates| RequestDto
    Connector -->|Sends via CurlSender| ExternalAPI
    ExternalAPI -->|Returns data| Connector
    Connector -->|Updates| ProcessDto
    Connector -->|Returns| Router
    Router -->|HTTP Response| Topology

Class Hierarchy #

ANode
  ↓
ACommonConnector (provides HTTP client)
  ↓
ACommonNode (integrates with Orchesty)
  ↓
AConnector (you extend this)
  ↓
YourConnector (your implementation)

Connector Lifecycle #

sequenceDiagram
    participant O as Orchesty
    participant R as Router
    participant C as Connector
    participant A as Application
    participant API as External API
    
    O->>R: POST /connector/my-connector/action
    R->>R: Create ProcessDto from request
    R->>C: processAction(dto)
    C->>A: getApplicationInstallFromProcess(dto)
    A-->>C: ApplicationInstall with credentials
    C->>C: Create RequestDto
    C->>API: HTTP Request
    API-->>C: HTTP Response
    C->>C: Process response
    C->>C: dto.setJsonData(result)
    C-->>R: Return dto
    R-->>O: HTTP Response with ProcessDto

Implementation #

Basic Connector Structure #

Every connector extends AConnector and implements two required methods:

import AConnector from '@orchesty/nodejs-sdk/lib/Connector/AConnector';
import ProcessDto from '@orchesty/nodejs-sdk/lib/Utils/ProcessDto';

export default class MyConnector extends AConnector {
    
    public getName(): string {
        return 'my-connector';  // Unique identifier
    }
    
    public async processAction(dto: ProcessDto): Promise<ProcessDto> {
        // 1. Get input data
        const input = dto.getJsonData();
        
        // 2. Perform your logic
        const result = await this.doSomething(input);
        
        // 3. Set output data
        dto.setJsonData(result);
        
        // 4. Return the modified DTO
        return dto;
    }
    
    private async doSomething(input: any): Promise<any> {
        // Your business logic here
        return { processed: true, data: input };
    }
}

Connector with API Call #

Most connectors call external APIs:

import AConnector from '@orchesty/nodejs-sdk/lib/Connector/AConnector';
import ProcessDto from '@orchesty/nodejs-sdk/lib/Utils/ProcessDto';
import RequestDto from '@orchesty/nodejs-sdk/lib/Transport/Curl/RequestDto';
import { HttpMethods } from '@orchesty/nodejs-sdk/lib/Transport/HttpMethods';

export default class GetCustomerConnector extends AConnector {
    
    public getName(): string {
        return 'crm-get-customer';
    }
    
    public async processAction(dto: ProcessDto): Promise<ProcessDto> {
        // Get customer ID from input
        const { customerId } = dto.getJsonData();
        
        // Create API request
        const requestDto = new RequestDto(
            `https://api.example.com/customers/${customerId}`,
            HttpMethods.GET,
            dto
        );
        
        // Send request
        const response = await this.getSender().send(requestDto);
        
        // Parse and set response
        const customer = JSON.parse(response.getBody());
        dto.setJsonData(customer);
        
        return dto;
    }
}

Connector with Authentication #

Connectors work with Applications to handle authentication:

import AConnector from '@orchesty/nodejs-sdk/lib/Connector/AConnector';
import ProcessDto from '@orchesty/nodejs-sdk/lib/Utils/ProcessDto';
import RequestDto from '@orchesty/nodejs-sdk/lib/Transport/Curl/RequestDto';
import { HttpMethods } from '@orchesty/nodejs-sdk/lib/Transport/HttpMethods';
import CoreFormsEnum from '@orchesty/nodejs-sdk/lib/Application/Base/CoreFormsEnum';

export default class AuthenticatedConnector extends AConnector {
    
    public getName(): string {
        return 'api-get-data';
    }
    
    public async processAction(dto: ProcessDto): Promise<ProcessDto> {
        // Get application credentials for current user
        const appInstall = await this.getApplicationInstallFromProcess(dto);
        const settings = appInstall.getSettings();
        const apiKey = settings[CoreFormsEnum.AUTHORIZATION_FORM]['api_key'];
        
        // Create authenticated request
        const requestDto = new RequestDto(
            'https://api.example.com/protected/data',
            HttpMethods.GET,
            dto
        );
        
        requestDto.setHeaders({
            'Authorization': `Bearer ${apiKey}`,
            'Content-Type': 'application/json'
        });
        
        // Send request and process response
        const response = await this.getSender().send(requestDto);
        dto.setData(response.getBody());
        
        return dto;
    }
}

POST Request with JSON Body #

export default class CreateOrderConnector extends AConnector {
    
    public getName(): string {
        return 'shop-create-order';
    }
    
    public async processAction(dto: ProcessDto): Promise<ProcessDto> {
        const input = dto.getJsonData();
        
        // Create POST request
        const requestDto = new RequestDto(
            'https://api.shop.com/orders',
            HttpMethods.POST,
            dto
        );
        
        // Set JSON body
        requestDto.setJsonBody({
            customer_id: input.customerId,
            items: input.items,
            total: input.total,
            currency: 'USD'
        });
        
        // Set headers
        requestDto.setHeaders({
            'Content-Type': 'application/json',
            'X-API-Key': input.apiKey
        });
        
        // Send and process
        const response = await this.getSender().send(requestDto);
        const createdOrder = JSON.parse(response.getBody());
        
        dto.setJsonData({
            orderId: createdOrder.id,
            status: 'created',
            createdAt: new Date().toISOString()
        });
        
        return dto;
    }
}

Key Methods Available to Connectors #

From AConnector #

getSender() #

Returns the HTTP client for making API requests.

const response = await this.getSender().send(requestDto);

getApplication() #

Gets the Application instance associated with this connector.

const app = this.getApplication<MyApplication>();
const baseUrl = app.getBaseUrl();

getApplicationInstallFromProcess() #

Gets user credentials and settings.

const appInstall = await this.getApplicationInstallFromProcess(dto);
const apiKey = appInstall.getSettings()[CoreFormsEnum.AUTHORIZATION_FORM]['api_key'];

getDbClient() #

Access to database repositories.

const db = this.getDbClient();
const users = await db.getUserRepository().findMany({ active: true });

Registration and Discovery #

Registering Connectors #

Connectors must be registered in the DI container to be discovered:

import DIContainer from '@orchesty/nodejs-sdk/lib/DIContainer/Container';
import GetCustomerConnector from './connectors/GetCustomerConnector';
import CreateOrderConnector from './connectors/CreateOrderConnector';

const container = new DIContainer();

// Register connectors
container.setConnector(new GetCustomerConnector());
container.setConnector(new CreateOrderConnector());

// Start the SDK server
container.listen();

Discovery #

Once registered, Orchesty discovers your connectors via the /connector/list endpoint:

GET http://localhost:8080/connector/list

Response:

[
  {
    "name": "crm-get-customer",
    "type": "connector"
  },
  {
    "name": "shop-create-order",
    "type": "connector"
  }
]

Execution #

Orchesty executes connectors by calling:

POST http://localhost:8080/connector/{name}/action

Best Practices #

1. Keep Connectors Focused #

Each connector should do one thing well:

Good:

  • GetContactConnector - Fetches a single contact
  • ListContactsConnector - Lists multiple contacts
  • CreateContactConnector - Creates a contact

Bad:

  • ContactConnector - Does everything (get, list, create, update, delete)

2. Handle Errors Gracefully #

public async processAction(dto: ProcessDto): Promise<ProcessDto> {
    try {
        const response = await this.callApi();
        dto.setJsonData(response);
    } catch (error) {
        if (error.response?.status === 404) {
            dto.setStopProcess(
                ResultCode.STOP_AND_FAILED,
                `Resource not found: ${error.message}`
            );
        } else {
            // Let other errors be retried
            throw error;
        }
    }
    return dto;
}

3. Use Application Methods When Available #

If using an Application, leverage its methods:

public async processAction(dto: ProcessDto): Promise<ProcessDto> {
    const appInstall = await this.getApplicationInstallFromProcess(dto);
    const app = this.getApplication<MyApplication>();
    
    // Let the application create the authenticated request
    const requestDto = await app.getRequestDto(
        dto,
        appInstall,
        HttpMethods.GET,
        '/api/data'
    );
    
    const response = await this.getSender().send(requestDto);
    dto.setData(response.getBody());
    
    return dto;
}

4. Validate Input Data #

public async processAction(dto: ProcessDto): Promise<ProcessDto> {
    const input = dto.getJsonData();
    
    // Validate required fields
    if (!input.customerId) {
        dto.setStopProcess(
            ResultCode.STOP_AND_FAILED,
            'Missing required field: customerId'
        );
        return dto;
    }
    
    // Continue with processing
    // ...
}

5. Set Descriptive Success Messages #

dto.setJsonData(result);
dto.setSuccessProcess(`Successfully fetched customer ${customerId}`);
return dto;

6. Use Timeouts for Slow APIs #

const requestDto = new RequestDto(url, HttpMethods.GET, dto);
requestDto.setTimeout(120000); // 2 minute timeout

const response = await this.getSender().send(requestDto);

Common Patterns #

Pattern: Transform Data Between Systems #

export default class DataTransformerConnector extends AConnector {
    
    public getName(): string {
        return 'transform-customer-data';
    }
    
    public async processAction(dto: ProcessDto): Promise<ProcessDto> {
        const input = dto.getJsonData();
        
        // Transform from CRM format to ERP format
        const transformed = {
            client_id: input.customerId,
            client_name: `${input.firstName} ${input.lastName}`,
            contact_email: input.email,
            phone_number: input.phone,
            address: {
                street: input.address.line1,
                city: input.address.city,
                country: input.address.country
            }
        };
        
        dto.setJsonData(transformed);
        return dto;
    }
}

Pattern: Enrich Data with External API #

export default class EnrichContactConnector extends AConnector {
    
    public getName(): string {
        return 'enrich-contact';
    }
    
    public async processAction(dto: ProcessDto): Promise<ProcessDto> {
        const contact = dto.getJsonData();
        
        // Fetch additional data from enrichment API
        const requestDto = new RequestDto(
            `https://enrichment-api.com/enrich?email=${contact.email}`,
            HttpMethods.GET,
            dto
        );
        
        const response = await this.getSender().send(requestDto);
        const enrichmentData = JSON.parse(response.getBody());
        
        // Merge original data with enrichment data
        dto.setJsonData({
            ...contact,
            company: enrichmentData.company,
            title: enrichmentData.title,
            linkedIn: enrichmentData.linkedIn
        });
        
        return dto;
    }
}

Pattern: Conditional Logic #

export default class ConditionalConnector extends AConnector {
    
    public getName(): string {
        return 'route-by-priority';
    }
    
    public async processAction(dto: ProcessDto): Promise<ProcessDto> {
        const order = dto.getJsonData();
        
        // Add routing information based on conditions
        if (order.total > 1000) {
            dto.setForceFollowers('high-priority-handler');
        } else if (order.total > 100) {
            dto.setForceFollowers('normal-priority-handler');
        } else {
            dto.setForceFollowers('low-priority-handler');
        }
        
        return dto;
    }
}

Testing Connectors #

Unit Testing #

import GetCustomerConnector from '../GetCustomerConnector';
import ProcessDto from '@orchesty/nodejs-sdk/lib/Utils/ProcessDto';

describe('GetCustomerConnector', () => {
    it('should fetch customer data', async () => {
        const connector = new GetCustomerConnector();
        const dto = new ProcessDto();
        
        dto.setJsonData({ customerId: '123' });
        
        const result = await connector.processAction(dto);
        const data = result.getJsonData();
        
        expect(data).toHaveProperty('id', '123');
        expect(data).toHaveProperty('name');
    });
});

Testing via HTTP #

Test if connector is registered:

curl http://localhost:8080/connector/my-connector/action/test

Execute connector:

curl -X POST http://localhost:8080/connector/my-connector/action \
  -H "Content-Type: application/json" \
  -d '{"customerId": "123"}'

API References #

Next Steps #

  1. Read about Authentication to understand how connectors access credentials
  2. Learn about Data Flow to master ProcessDto manipulation
  3. Explore Pagination for handling large datasets
  4. Check the API Reference for AConnector for complete method documentation
© 2025 Orchesty Solutions. All rights reserved.