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:
| Aspect | Connector | Custom Node |
|---|---|---|
| Purpose | Call external APIs/services | Transform, filter, route data |
| HTTP support | Built-in via getSender() | None (you can add manually) |
| Authentication | Integrated with Applications | None (pure data processing) |
| Rate limiting | Automatic via Limiter | Not applicable |
| Retry policy | Built-in for API failures | Manual if needed |
| Base class | AConnector | ACommonNode |
| Use for | API calls, external services | Data 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 contactListContactsConnector- Lists multiple contactsCreateContactConnector- 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"}'
Related Concepts #
- Custom Node - For data transformation without API calls
- Authentication - How to use Applications with connectors
- Data Flow - Understanding ProcessDto lifecycle
- Error Handling - Handling failures in connectors
- Retry Policy - Automatic retries for failed requests
- Rate Limiting - Preventing API rate limit issues
API References #
- AConnector - Base connector class
- ProcessDto - Data transfer object
- RequestDto - HTTP request configuration
- DIContainer - Dependency injection container
- ApplicationInstall - User credentials and settings
Next Steps #
- Read about Authentication to understand how connectors access credentials
- Learn about Data Flow to master ProcessDto manipulation
- Explore Pagination for handling large datasets
- Check the API Reference for AConnector for complete method documentation