ACommonNode

Overview #

ACommonNode is the base class for creating custom processing nodes. Custom nodes are similar to connectors but can be used for general-purpose data processing, transformation, or utility functions that don't fit the connector pattern.

Purpose:

  • Create custom data processing logic
  • Transform data between steps
  • Implement utility functions
  • Build specialized processing nodes

Location: orchesty-nodejs-sdk/lib/Commons/ACommonNode.ts

When to Use #

Use ACommonNode for:

  • Data transformation (format conversion, filtering, mapping)
  • Utility functions (validation, enrichment, calculation)
  • Custom logic that doesn't fit connector or batch patterns
  • Processing nodes without external API calls

Difference from AConnector:

  • ACommonNode: General-purpose processing, typically no external API calls
  • AConnector: Specifically for integrating with external services/APIs

Class Hierarchy #

ANode
ACommonNode (You extend this)
YourCustomNode

Abstract Methods You Must Implement #

processAction() #

public abstract processAction(dto: ProcessDto): ProcessDto | Promise<ProcessDto>

Main method where you implement your custom processing logic.

Parameters:

ParameterTypeDescription
dtoProcessDtoProcess data transfer object

Returns: ProcessDto | Promise<ProcessDto> - Modified process DTO

Pattern:

public processAction(dto: ProcessDto): ProcessDto {
    // 1. Get input data
    const input = dto.getJsonData();
    
    // 2. Process/transform data
    const output = this.transformData(input);
    
    // 3. Set output data
    dto.setJsonData(output);
    
    // 4. Return modified DTO
    return dto;
}

getName() #

public abstract getName(): string

Returns the unique identifier for your custom node.

Example:

public getName(): string {
    return 'data-transformer';
}

Inherited Methods Available #

getApplication() #

protected getApplication<T extends IApplication>(): T

Returns the associated application instance (if set).

getDbClient() #

protected getDbClient(): DatabaseClient

Returns the database client for accessing repositories.

getApplicationInstallFromProcess() #

protected async getApplicationInstallFromProcess(dto: AProcessDto): Promise<ApplicationInstall>

Gets application credentials for the current user.

Usage Examples #

Basic Custom Node #

From: orchesty-nodejs-sdk/test/CustomNode/TestCustomNode.ts

import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';

export default class TestCustomNode extends ACommonNode {

    public getName(): string {
        return 'testcustom';
    }

    public processAction(dto: ProcessDto): ProcessDto {
        // Transform data
        dto.setJsonData({ 
            test: 'custom', 
            inner: { 
                date: Date.now().toString(), 
                one: 2 
            } 
        });

        return dto;
    }
}

Data Transformation Node #

import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';

export default class DataTransformerNode extends ACommonNode {

    public getName(): string {
        return 'data-transformer';
    }

    public processAction(dto: ProcessDto): ProcessDto {
        const input = dto.getJsonData();
        
        // Transform data structure
        const output = {
            transformedAt: new Date().toISOString(),
            originalData: input,
            processed: {
                // Map fields
                fullName: `${input.firstName} ${input.lastName}`,
                email: input.email.toLowerCase(),
                age: this.calculateAge(input.birthDate),
                tags: this.extractTags(input)
            }
        };
        
        dto.setJsonData(output);
        return dto;
    }

    private calculateAge(birthDate: string): number {
        const birth = new Date(birthDate);
        const today = new Date();
        let age = today.getFullYear() - birth.getFullYear();
        const monthDiff = today.getMonth() - birth.getMonth();
        
        if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
            age--;
        }
        
        return age;
    }

    private extractTags(data: any): string[] {
        const tags: string[] = [];
        if (data.isPremium) tags.push('premium');
        if (data.newsletter) tags.push('newsletter');
        return tags;
    }
}

Data Validation Node #

import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';
import ResultCode from '../../lib/Utils/ResultCode';

export default class DataValidatorNode extends ACommonNode {

    public getName(): string {
        return 'data-validator';
    }

    public processAction(dto: ProcessDto): ProcessDto {
        const input = dto.getJsonData();
        
        // Validate required fields
        const requiredFields = ['email', 'name', 'phone'];
        const missingFields: string[] = [];
        
        requiredFields.forEach(field => {
            if (!input[field]) {
                missingFields.push(field);
            }
        });
        
        if (missingFields.length > 0) {
            dto.setStopProcess(
                ResultCode.STOP_AND_FAILED,
                `Missing required fields: ${missingFields.join(', ')}`
            );
            return dto;
        }
        
        // Validate email format
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        if (!emailRegex.test(input.email)) {
            dto.setStopProcess(
                ResultCode.STOP_AND_FAILED,
                'Invalid email format'
            );
            return dto;
        }
        
        // Validation passed
        dto.setJsonData({
            ...input,
            validated: true,
            validatedAt: new Date().toISOString()
        });
        
        return dto;
    }
}

Data Filter Node #

import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';

export default class DataFilterNode extends ACommonNode {

    public getName(): string {
        return 'data-filter';
    }

    public processAction(dto: ProcessDto): ProcessDto {
        const input = dto.getJsonData();
        
        // Filter out items based on criteria
        const filtered = input.items.filter((item: any) => {
            // Keep only active items with positive value
            return item.status === 'active' && item.value > 0;
        });
        
        dto.setJsonData({
            filteredItems: filtered,
            originalCount: input.items.length,
            filteredCount: filtered.length,
            removedCount: input.items.length - filtered.length
        });
        
        return dto;
    }
}

Data Enrichment Node #

import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';

export default class DataEnrichmentNode extends ACommonNode {

    public getName(): string {
        return 'data-enrichment';
    }

    public processAction(dto: ProcessDto): ProcessDto {
        const input = dto.getJsonData();
        
        // Enrich data with additional information
        const enriched = {
            ...input,
            metadata: {
                processedAt: new Date().toISOString(),
                processorVersion: '1.0.0',
                environment: process.env.NODE_ENV || 'development'
            },
            computed: {
                totalValue: this.calculateTotal(input.items),
                averageValue: this.calculateAverage(input.items),
                itemCount: input.items.length
            }
        };
        
        dto.setJsonData(enriched);
        return dto;
    }

    private calculateTotal(items: any[]): number {
        return items.reduce((sum, item) => sum + (item.value || 0), 0);
    }

    private calculateAverage(items: any[]): number {
        if (items.length === 0) return 0;
        return this.calculateTotal(items) / items.length;
    }
}

Conditional Router Node #

import ACommonNode from '../../lib/Commons/ACommonNode';
import ProcessDto from '../../lib/Utils/ProcessDto';

export default class ConditionalRouterNode extends ACommonNode {

    public getName(): string {
        return 'conditional-router';
    }

    public processAction(dto: ProcessDto): ProcessDto {
        const input = dto.getJsonData();
        
        // Route based on conditions
        if (input.priority === 'high') {
            dto.setForceFollowers('high-priority-handler');
        } else if (input.value > 1000) {
            dto.setForceFollowers('large-value-handler');
        } else {
            dto.setForceFollowers('default-handler');
        }
        
        // Add routing information
        dto.setJsonData({
            ...input,
            routedBy: 'conditional-router',
            routedAt: new Date().toISOString()
        });
        
        return dto;
    }
}

Common Patterns #

Pattern 1: Simple Transformation #

public processAction(dto: ProcessDto): ProcessDto {
    const input = dto.getJsonData();
    const output = this.transform(input);
    dto.setJsonData(output);
    return dto;
}

Pattern 2: Validation with Error Handling #

public processAction(dto: ProcessDto): ProcessDto {
    const input = dto.getJsonData();
    
    const errors = this.validate(input);
    if (errors.length > 0) {
        dto.setStopProcess(
            ResultCode.STOP_AND_FAILED,
            `Validation failed: ${errors.join(', ')}`
        );
        return dto;
    }
    
    dto.setJsonData({ ...input, validated: true });
    return dto;
}

Pattern 3: Enrichment #

public processAction(dto: ProcessDto): ProcessDto {
    const input = dto.getJsonData();
    
    const enrichedData = {
        ...input,
        enrichments: this.addEnrichments(input)
    };
    
    dto.setJsonData(enrichedData);
    return dto;
}

Registration #

import DIContainer from './DIContainer/Container';
import DataTransformerNode from './nodes/DataTransformerNode';
import DataValidatorNode from './nodes/DataValidatorNode';

const container = new DIContainer();

// Register custom nodes
const transformerNode = new DataTransformerNode();
const validatorNode = new DataValidatorNode();

container.setCustomNode(transformerNode);
container.setCustomNode(validatorNode);

Next Steps #

  1. ProcessDto - Work with data in nodes
  2. AConnector - Create connectors for APIs
  3. ABatchNode - Create batch processing nodes
  4. DIContainer - Register custom nodes

See Also #

© 2025 Orchesty Solutions. All rights reserved.