Skip to content

Extensions

graju256 edited this page Dec 22, 2025 · 16 revisions

Extensions

Graphman Client provides an extensibility mechanism that allows you to customize and enhance the behavior of various operations through extension points. Extensions are JavaScript modules that implement specific interfaces to intercept and modify data at key points in the Graphman workflow.

What are Extensions?

Extensions are pluggable JavaScript modules that allow you to:

  • Modify HTTP request options before sending to the Gateway
  • Process input/output bundles
  • Process input/output details by loading third-party modules if required

How Extensions Work

  1. Registration: Extensions are registered in the graphman.configuration file
  2. Loading: Graphman loads extension modules at runtime
  3. Invocation: Extensions are invoked at specific points during operation execution
  4. Processing: Each extension receives input data and context, processes it, and returns modified data

Extension Architecture

┌─────────────────┐
│  Graphman CLI   │
└────────┬────────┘
         │
         ├──> pre-request ──────> HTTP Request ──────> Gateway
         │                                                │
         │                                                ▼
         │                                          Query/Mutation
         │                                                │
         │                                                ▼
         ├──> post-export <────── Export Response <──────┘
         │
         ├──> pre-import ───────> Import Request ──────> Gateway
         │
         ├──> multiline-text-diff ──> Diff Operation
         │
         └──> policy-code-validator ──> Validate Operation

Note

Extensions can only be activated/processed from the configured home directory. Please consider configuring your workspace (home) directory for hosting the extensions. Ref: https://github.com/Layer7-Community/graphman-client/wiki/Getting-Started#configure-home-directory

Note

Extension argument (context) is revised since 2.0.9 release.

Available Extensions

1. pre-request

Purpose: Modify HTTP request options before sending to the Gateway

When Invoked: Before every GraphQL query or mutation request

Use Cases:

  • Add custom HTTP headers
  • Modify authentication credentials
  • Configure proxy settings
  • Add request interceptors
  • Implement custom retry logic

2. post-export

Purpose: Process and transform bundles after exporting from the Gateway

When Invoked: After receiving export response from the Gateway, before writing to output

Use Cases:

  • Remove sensitive data from exported bundles
  • Transform entity properties
  • Add metadata or annotations
  • Filter out specific entities
  • Normalize data formats

3. pre-import

Purpose: Process and transform bundles before importing to the Gateway

When Invoked: Before sending import mutation to the Gateway

Use Cases:

  • Sanitize bundle data
  • Replace environment-specific values
  • Validate bundle structure
  • Transform entity properties
  • Add or remove entities based on rules

4. multiline-text-diff

Purpose: Compute detailed differences for multiline text fields (especially policy code)

When Invoked: During diff operation when comparing entities with multiline text fields

Use Cases:

  • Generate unified diff format for policy XML
  • Customize diff output format
  • Ignore whitespace differences
  • Highlight specific changes
  • Generate HTML diff reports

Pre-requisites:

  • default implementation uses the diff third-party module. By default, it is not enabled for use.
  • once enabled, make sure this package is installed, available for use
    • npm install diff@5.2.0 --global

5. policy-code-validator

Purpose: Create custom JSON schema validators for policy code validation

When Invoked: During validate operation when validating policy code

Use Cases:

  • Use different JSON schema validator libraries
  • Configure validator options
  • Add custom validation formats
  • Implement custom validation logic

Pre-requisites:

  • default implementation uses the ajv third-party module. By default, it is not enabled for use.
  • once enabled, make sure this package is installed, available for use
    • npm install ajv --global

Note

Not all the extensions are enabled by default for use. Needed extensions can be enabled either by configuration file or using --options.extensions CLI argument. When the extensions are customized and configured to load from the home directory, their third-party packages might not get loaded. Because of which, the below warning can be observed.

[warn] failed to load the multiline-text-diff extension, cause=MODULE_NOT_FOUND, falling back to the default

This can be resolved by linking the required third-party packages to the home directory using the below npm-link command.

;change the current directory to GRAPHMAN_HOME
npm link <third-party-library-name>

Example: How to avoid exporting unnecessary dependencies

Assuming that the graphman is configured with home directory so that extensions are pulled into it with the default code. FYI, refer to https://github.com/Layer7-Community/graphman-client/wiki/Getting-Started#configure-home-directory

Edit the $GRAPHMAN_HOME/modules/graphman-extension-post-export.js file and replace the existing code with the below one.

/*
 * Copyright (c)  2025. Broadcom Inc. and/or its subsidiaries. All Rights Reserved.
 */

module.exports = {
    /**
     * Extension to process gateway exported configuration.
     * @param input exported bundle
     * @param context CLI operation execution context
     * @param context.operation operation
     * @param context.gateway gateway object
     * @param context.options CLI options
     */
    apply: function (input, context) {
        if (context.options.excludeOTK) {
            if (input.policies) input.policies = input.policies.filter(item => !item.folderPath || !item.folderPath.startsWith("/OTK"));
            if (input.encassConfigs) input.encassConfigs = input.encassConfigs.filter(item => !item.name.startsWith("OTK "));
            if (input.serverModuleFiles) input.serverModuleFiles = input.serverModuleFiles.filter(item => !item.name.startsWith("OTK "));
            if (input.clusterProperties) input.clusterProperties = input.clusterProperties.filter(item => !item.name.startsWith("otk."));
        }
        
        return input;
    }
}

Now, run the export command with --options.excludeOTK option

graphman export --service:full --variables.resolutionPath /some-service --options.excludeOTK

Example: How to customize graphman request

Assuming that the graphman is configured with home directory so that extensions are pulled into it with the default code. FYI, refer to https://github.com/Layer7-Community/graphman-client/wiki/Getting-Started#configure-home-directory

Edit the $GRAPHMAN_HOME/modules/graphman-extension-pre-request.js file and replace the existing code with the below one.

/*
 * Copyright (c)  2025. Broadcom Inc. and/or its subsidiaries. All Rights Reserved.
 */

module.exports = {
    /**
     * Extension to enhance http request options
     * @param input http request options
     * @param context CLI operation execution context
     * @param context.operation operation
     * @param context.gateway gateway object
     * @param context.options CLI options
     */
    apply: function (input, context) {
        // Add your custom header to the HTTP request
        input.headers["x-hello"] = "Hello, World!";
        
        // Assuming that --options.customGreetings CLI argument is specified.
        input.headers["y-hello"] = context.options.customGreetings;
        return input;
    }
}

Now, run the export command with --options.customGreetings option

graphman export --service:full --variables.resolutionPath /some-service --options.customGreetings "Hello, Graphman!"

Best Practices

1. Keep Extensions Simple

Extensions should focus on a single responsibility:

  • ✅ Transform specific fields
  • ✅ Add metadata
  • ✅ Validate structure
  • ❌ Complex business logic
  • ❌ External API calls (unless necessary)

2. Handle Errors Gracefully

module.exports = {
    apply: function (input, context) {
        try {
            // Your logic here
            return processInput(input, context);
        } catch (error) {
            console.error('Extension error:', error.message);
            // Return original input on error
            return input;
        }
    }
}

3. Use Logging

module.exports = {
    apply: function (input, context) {
        console.log(`[${context.operation}] Processing with custom extension`);
        
        // Your logic
        
        console.log(`[${context.operation}] Extension complete`);
        return input;
    }
}

4. Make Extensions Configurable

module.exports = {
    apply: function (input, context) {
        const config = context.options.extensionConfig || {};
        
        if (config.enableFeatureX) {
            // Feature X logic
        }
        
        return input;
    }
}

Pass configuration via command line:

graphman export --using all --extensionConfig.enableFeatureX true

Clone this wiki locally