Routing
What is Routing? #
Routing in Orchesty SDK refers to the HTTP endpoint system that allows the Orchesty platform to communicate with your integration code. When the orchestration layer needs to execute a connector or application action, it makes HTTP requests to specific endpoints that the SDK automatically creates and manages.
Key concepts:
- HTTP endpoints: URLs that trigger your connectors and applications
- Router classes: Components that define and handle endpoints
- Express.js: Web framework powering the SDK's HTTP server
- Endpoint discovery: How Orchesty finds available connectors
- Request/Response flow: How data moves through HTTP layer
Why Understanding Routing Matters #
Understanding routing helps you:
- Debug issues: Know where requests come from and where they go
- Test locally: Call your connectors directly via HTTP
- Monitor traffic: Understand what's happening in your integration
- Troubleshoot errors: Trace HTTP-level problems
- Extend SDK: Add custom endpoints if needed
How Routing Works #
Request Flow #
sequenceDiagram
participant O as Orchesty Platform
participant R as Router
participant C as Connector/Application
participant A as External API
O->>R: POST /connector/my-connector/action
R->>R: Parse request, create ProcessDto
R->>C: Call processAction(dto)
C->>A: Make API calls
A-->>C: Return data
C->>C: Transform data
C-->>R: Return ProcessDto
R->>R: Format response
R-->>O: Return HTTP response
Router Architecture #
graph TB
Express[Express App]
Express --> CR[ConnectorRouter]
Express --> AR[ApplicationRouter]
Express --> BR[BatchRouter]
Express --> WR[WebhookRouter]
Express --> CNR[CustomNodeRouter]
CR --> C1[Connector 1]
CR --> C2[Connector 2]
AR --> A1[Application 1]
AR --> A2[Application 2]
BR --> B1[Batch Node 1]
style Express fill:#e1f5ff
style CR fill:#fff4e1
style AR fill:#fff4e1
style BR fill:#fff4e1
SDK Endpoints #
Connector Endpoints #
Execute Connector #
POST /connector/{name}/action
Content-Type: application/json
{
"data": {...},
"headers": {...}
}
Purpose: Execute a specific connector
Example:
curl -X POST http://localhost:8080/connector/get-customer/action \
-H "Content-Type: application/json" \
-d '{"customerId": "123"}'
Test Connector #
GET /connector/{name}/action/test
Purpose: Check if connector is registered and loadable
Example:
curl http://localhost:8080/connector/get-customer/action/test
List Connectors #
GET /connector/list
Purpose: Get all registered connectors
Response:
[
{
"name": "get-customer",
"type": "connector"
},
{
"name": "create-order",
"type": "connector"
}
]
Application Endpoints #
List Applications #
GET /application/list
Purpose: Get all registered applications
Get Application Details #
GET /application/{name}
Purpose: Get application metadata (name, logo, description)
Get Authorization Form #
GET /application/{name}/form
Purpose: Get the credential form for user to fill out
Authorize Application #
GET /application/{name}/authorize?user={email}
Purpose: Start OAuth2 authorization flow
Check Authorization Status #
GET /application/{name}/users/{email}/authorized
Purpose: Check if user has valid credentials
Test Application #
GET /application/{name}/test
Purpose: Check if application is registered
Batch Endpoints #
Execute Batch Node #
POST /batch/{name}/action
Purpose: Execute a batch processing node
Test Batch Node #
GET /batch/{name}/action/test
Purpose: Check if batch node is registered
List Batch Nodes #
GET /batch/list
Purpose: Get all registered batch nodes
Webhook Endpoints #
Subscribe Webhook #
POST /webhook/applications/{name}/users/{user}/subscribe
Content-Type: application/json
{
"name": "webhook-name",
"topology": "topology-id"
}
Purpose: Subscribe to webhook events
Unsubscribe Webhook #
POST /webhook/applications/{name}/users/{user}/unsubscribe
Purpose: Unsubscribe from webhook events
List Webhooks #
GET /webhook/applications/{name}/users/{user}
Purpose: Get user's active webhooks
Custom Node Endpoints #
Execute Custom Node #
POST /custom-node/{name}/action
Purpose: Execute a custom node
List Custom Nodes #
GET /custom-node/list
Purpose: Get all registered custom nodes
Router Classes #
ConnectorRouter #
Handles connector execution:
// From: orchesty-nodejs-sdk/lib/Connector/ConnectorRouter.ts
export default class ConnectorRouter extends ACommonRouter {
public configureRoutes(): express.Application {
// Execute connector
this.app.route('/connector/:name/action').post(async (req, res, next) => {
try {
const connector = this.loader.get(CONNECTOR_PREFIX, req.params.name);
const dto = await connector.processAction(
await createProcessDto(req, connector.getApplicationName())
);
createSuccessResponse(res, dto);
next();
} catch (e) {
next(e);
}
});
// Test connector
this.app.route('/connector/:name/action/test').get(async (req, res, next) => {
try {
await this.loader.get(CONNECTOR_PREFIX, req.params.name);
res.json([]);
next();
} catch (e) {
createApiErrorResponse(req, res, e);
}
});
// List connectors
this.app.route('/connector/list').get((req, res, next) => {
try {
res.json(this.loader.getList(CONNECTOR_PREFIX));
next();
} catch (e) {
createApiErrorResponse(req, res, e);
}
});
return this.app;
}
}
ApplicationRouter #
Handles application operations:
// Simplified structure
export default class ApplicationRouter extends ACommonRouter {
public configureRoutes(): express.Application {
// List applications
this.app.route('/application/list').get(...);
// Get application details
this.app.route('/application/:name').get(...);
// Get form
this.app.route('/application/:name/form').get(...);
// Authorize
this.app.route('/application/:name/authorize').get(...);
// Check authorized
this.app.route('/application/:name/users/:user/authorized').get(...);
return this.app;
}
}
BatchRouter #
Handles batch node execution:
export default class BatchRouter extends ACommonRouter {
public configureRoutes(): express.Application {
// Execute batch
this.app.route('/batch/:name/action').post(...);
// Test batch
this.app.route('/batch/:name/action/test').get(...);
// List batches
this.app.route('/batch/list').get(...);
return this.app;
}
}
Request Processing #
Creating ProcessDto from Request #
The SDK automatically converts HTTP requests to ProcessDto:
// Incoming HTTP request:
{
"body": "{\"customerId\":\"123\"}",
"headers": {
"content-type": "application/json",
"correlation-id": "corr-abc-123",
"node-id": "node-xyz-456"
}
}
// Becomes ProcessDto:
const dto = new ProcessDto();
dto.setData('{"customerId":"123"}');
dto.setHeaders({
'content-type': 'application/json',
'correlation-id': 'corr-abc-123',
'node-id': 'node-xyz-456'
});
Creating HTTP Response from ProcessDto #
The SDK converts ProcessDto back to HTTP response:
// ProcessDto after processing:
dto.setJsonData({ customer: {...} });
dto.setSuccessProcess('Customer fetched');
// Becomes HTTP response:
{
"statusCode": 200,
"headers": {
"content-type": "application/json",
"correlation-id": "corr-abc-123"
},
"body": {
"data": "{\"customer\":{...}}",
"headers": {...},
"result-code": "success",
"result-message": "Customer fetched"
}
}
Middleware #
The SDK uses Express middleware for cross-cutting concerns:
Error Handler #
Catches errors and formats error responses:
// From: ErrorHandler.ts
export default function errorHandler(nodeRepository: NodeRepository) {
return async (err: Error, req: Request, res: Response, next: NextFunction) => {
if (err instanceof OnRepeatException) {
// Handle retry
dto.setRepeater(err.getInterval(), err.getMaxHops(), err.message);
createSuccessResponse(res, dto);
} else if (err instanceof OnStopAndFailException) {
// Handle stop
createErrorResponse(res, dto, err);
} else {
// Unknown error
createErrorResponse(res, dto, err);
}
};
}
Metrics Handler #
Records metrics for requests:
// From: MetricsHandler.ts
export default function metricsHandler(req: Request, res: Response, next: NextFunction) {
const startMetrics = Metrics.getCurrentMetrics();
res.on('finish', () => {
// Send metrics after response
sendMetrics(req, res, startMetrics);
});
next();
}
Testing Endpoints Locally #
Testing Connector #
# Start SDK server
npm start
# 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",
"action": "fetch"
}'
Testing Application #
# List applications
curl http://localhost:8080/application/list
# Get application form
curl http://localhost:8080/application/shopify/form
# Check if authorized
curl http://localhost:8080/application/shopify/users/user@example.com/authorized
Testing Batch Node #
# List batch nodes
curl http://localhost:8080/batch/list
# Execute batch node
curl -X POST http://localhost:8080/batch/get-all-orders/action \
-H "Content-Type: application/json" \
-d '{
"startDate": "2024-01-01",
"endDate": "2024-12-31"
}'
Custom Routing #
Adding Custom Endpoints #
You can add custom endpoints by extending the SDK:
import express from 'express';
import ACommonRouter from '@orchesty/nodejs-sdk/lib/Commons/ACommonRouter';
export default class CustomRouter extends ACommonRouter {
public constructor(app: express.Application) {
super(app, 'CustomRouter');
}
public configureRoutes(): express.Application {
// Add custom health check endpoint
this.app.route('/health').get((req, res) => {
res.json({
status: 'healthy',
timestamp: new Date().toISOString(),
uptime: process.uptime()
});
});
// Add custom metrics endpoint
this.app.route('/metrics').get((req, res) => {
res.json({
connectors: this.getConnectorCount(),
applications: this.getApplicationCount(),
memory: process.memoryUsage()
});
});
return this.app;
}
private getConnectorCount(): number {
// Implementation
return 0;
}
private getApplicationCount(): number {
// Implementation
return 0;
}
}
Registering Custom Router #
import DIContainer from '@orchesty/nodejs-sdk/lib/DIContainer/Container';
import CustomRouter from './routers/CustomRouter';
const container = new DIContainer();
// Get Express app
const app = container.getApp();
// Add custom router
new CustomRouter(app);
// Start server
container.listen();
Port Configuration #
Default Port #
The SDK runs on port 8080 by default:
Server listening on port 8080
Custom Port #
Set via environment variable:
PORT=3000 npm start
Or in code:
// Set port before starting
process.env.PORT = '3000';
const container = new DIContainer();
container.listen();
CORS and Security #
CORS Configuration #
For browser-based testing:
import cors from 'cors';
const container = new DIContainer();
const app = container.getApp();
// Enable CORS
app.use(cors({
origin: 'http://localhost:3000',
credentials: true
}));
container.listen();
Authentication #
The SDK doesn't enforce authentication at the HTTP level. Authentication is handled:
- For users: Via ApplicationInstall credentials
- For Orchesty: Via API keys in headers
- For webhooks: Via secure tokens in URLs
Monitoring and Debugging #
Request Logging #
All requests are automatically logged:
{
"message": "POST /connector/get-customer/action",
"statusCode": 200,
"duration": 234,
"correlationId": "corr-abc-123"
}
Error Responses #
Errors are formatted consistently:
{
"statusCode": 500,
"error": "Internal Server Error",
"message": "Failed to fetch customer: Network timeout",
"correlationId": "corr-abc-123"
}
Health Checks #
Check if SDK is running:
# Any endpoint will work, but /connector/list is lightweight
curl http://localhost:8080/connector/list
Common Routing Patterns #
Pattern: Authentication Middleware #
import express from 'express';
function requireApiKey(req: express.Request, res: express.Response, next: express.NextFunction) {
const apiKey = req.headers['x-api-key'];
if (!apiKey || apiKey !== process.env.API_KEY) {
res.status(401).json({ error: 'Unauthorized' });
return;
}
next();
}
// Apply to specific routes
app.use('/connector', requireApiKey);
Pattern: Rate Limiting Middleware #
import rateLimit from 'express-rate-limit';
const limiter = rateLimit({
windowMs: 60 * 1000, // 1 minute
max: 100, // 100 requests per minute
message: 'Too many requests from this IP'
});
app.use('/connector', limiter);
Pattern: Request Validation #
function validateRequest(req: express.Request, res: express.Response, next: express.NextFunction) {
if (!req.body || typeof req.body !== 'object') {
res.status(400).json({ error: 'Invalid request body' });
return;
}
next();
}
app.use('/connector/:name/action', validateRequest);
Troubleshooting #
Endpoint Not Found (404) #
Check:
- Is the connector/application registered in DIContainer?
- Is the name correct in the URL?
- Is the SDK server running?
- Check
/connector/listto see registered connectors
Internal Server Error (500) #
Check:
- Application logs for error messages
- Ensure connector/application code doesn't throw unhandled errors
- Verify all dependencies are available
Timeout Errors #
Check:
- Connector execution time (default timeout: 30s)
- External API response times
- Database query performance
CORS Errors #
Add CORS middleware (see CORS Configuration above)
Best Practices #
1. Use Descriptive Names #
// Good
this.app.route('/connector/:name/action')
this.app.route('/application/:name/authorize')
// Bad
this.app.route('/c/:n/a')
this.app.route('/app/:name/auth')
2. Handle Errors Properly #
this.app.route('/connector/:name/action').post(async (req, res, next) => {
try {
// Process request
} catch (error) {
next(error); // Pass to error handler
}
});
3. Return Consistent Responses #
// Success
res.json({
statusCode: 200,
data: result
});
// Error
res.status(500).json({
statusCode: 500,
error: 'Error message'
});
4. Log Requests #
import logger from '@orchesty/nodejs-sdk/lib/Logger/Logger';
this.app.route('/custom-endpoint').get((req, res) => {
logger.info(`Custom endpoint called: ${req.path}`, req);
// Handle request
});
5. Test Endpoints #
Always test your endpoints locally before deploying:
# Test connector
curl -v http://localhost:8080/connector/my-connector/action/test
# Test with data
curl -v -X POST http://localhost:8080/connector/my-connector/action \
-H "Content-Type: application/json" \
-d '{"test": "data"}'
Related Concepts #
- Connector - What gets called by routing
- Data Flow - How data moves through HTTP layer
- Logging - Monitoring HTTP requests
- Error Handling - HTTP error responses
- Webhooks - Webhook routing
API References #
- ConnectorRouter - Connector routing
- ApplicationRouter - Application routing
- BatchRouter - Batch routing
- ACommonRouter - Base router class
- DIContainer - Service container and startup
Next Steps #
- Learn about Connectors to understand what endpoints execute
- Explore Data Flow to see how ProcessDto works
- Read about Logging for monitoring requests
- Check DIContainer docs for SDK initialization