A comprehensive TypeScript toolkit for Adobe App Builder applications providing standardized Adobe Commerce integrations, I/O Events orchestration, file storage utilities, authentication helpers, and robust backend development tools with 100% test coverag
npm install @adobe-commerce/aio-toolkitA comprehensive TypeScript toolkit for Adobe App Builder applications providing standardized Adobe Commerce integrations, I/O Events orchestration, file storage utilities, authentication helpers, and robust backend development tools with 100% test coverage and enterprise-grade reliability.
``bash`
npm install @adobe-commerce/aio-toolkit
The toolkit is organized into five main modules:
Core infrastructure for Adobe App Builder applications
#### RuntimeAction
HTTP request handling and business logic execution for Adobe I/O Runtime.
`typescript
const {
RuntimeAction,
RuntimeActionResponse,
HttpMethod,
HttpStatus,
} = require('@adobe-commerce/aio-toolkit');
// Create a simple action
const myAction = RuntimeAction.execute(
'process-user', // Action name
[HttpMethod.POST], // Allowed HTTP methods
['userId'], // Required parameters
['authorization'], // Required headers
async (params, ctx) => {
// Your business logic here
const { userId } = params;
const { logger } = ctx;
logger.info(Processing request for user: ${userId});
// Return success response
return RuntimeActionResponse.success({
message: 'Action completed successfully',
userId: userId,
});
}
);
// Export for Adobe I/O Runtime
exports.main = myAction;
`
#### EventConsumerAction
Event-driven processing for Adobe I/O Events with automatic validation.
`typescript
const {
EventConsumerAction,
RuntimeActionResponse,
HttpStatus
} = require('@adobe-commerce/aio-toolkit');
// Create a simple event consumer
const myEventConsumer = EventConsumerAction.execute(
'commerce-event-handler',
['eventType'], // Required parameters
['x-adobe-signature'], // Required headers
async (params, ctx) => {
const { eventType } = params;
const { logger } = ctx;
logger.info(Processing event: ${eventType});
// Process the event
await processCommerceEvent(eventType, params);
// Return success response
return RuntimeActionResponse.success({
message: 'Event processed successfully',
eventType: eventType,
processedAt: new Date().toISOString()
});
}
);
// Export for Adobe I/O Runtime
exports.main = myEventConsumer;
`
#### WebhookAction
Secure webhook request handler with built-in Adobe Commerce signature verification.
`typescript
const {
WebhookAction,
WebhookActionResponse,
SignatureVerification
} = require('@adobe-commerce/aio-toolkit');
// Create a webhook action with signature verification enabled
const myWebhook = WebhookAction.execute(
'order-webhook',
['orderId'], // Required parameters
['x-adobe-commerce-webhook-id'], // Required headers
SignatureVerification.ENABLED, // Enable signature verification
async (params, ctx) => {
const { orderId } = params;
const { logger } = ctx;
logger.info(Processing order webhook: ${orderId});
// Your webhook logic here
// Return structured webhook response
return [
WebhookActionResponse.add('result', {
orderId: orderId,
status: 'processed',
timestamp: new Date().toISOString()
}),
WebhookActionResponse.success()
];
}
);
// Export for Adobe I/O Runtime
exports.main = myWebhook;
// Disable signature verification for testing
const testWebhook = WebhookAction.execute(
'test-webhook',
['data'],
[],
SignatureVerification.DISABLED,
async (params, ctx) => {
return WebhookActionResponse.success();
}
);
`
WebhookActionResponse Operations:
- success(): Indicates successful webhook processingexception(message?, exceptionClass?)
- : Returns error responseadd(path, value, instance?)
- : Adds data to responsereplace(path, value, instance?)
- : Replaces data in responseremove(path)
- : Removes data from response
#### PublishEvent
Event publishing component for Adobe I/O Events with CloudEvents support.
`typescript
const { PublishEvent } = require('@adobe-commerce/aio-toolkit');
// Initialize the publisher
const publishEvent = new PublishEvent(
'your-ims-org-id@AdobeOrg',
'your-api-key',
'your-access-token',
logger // Optional custom logger
);
// Publish a simple event
const result = await publishEvent.execute(
'your-provider-id',
'commerce.order.created',
{
orderId: 'ORD-123456',
customerId: 'CUST-789',
amount: 199.99,
currency: 'USD'
}
);
console.log(Event published: ${result.eventId}, Status: ${result.status});
// Publish an event with subject
const orderResult = await publishEvent.execute(
'commerce-provider',
'com.adobe.commerce.order.shipped',
{
orderId: 'ORD-123456',
trackingNumber: 'TRK-789',
carrier: 'UPS',
estimatedDelivery: '2023-12-05T18:00:00Z'
},
'orders/ORD-123456'
);
// Handle publishing results
if (orderResult.status === 'published') {
console.log(Order shipped event published successfully: ${orderResult.eventId});Failed to publish event: ${orderResult.error}
} else {
console.error();`
}
#### GraphQlAction
GraphQL server implementation with schema validation and introspection control.
`typescript
const { GraphQlAction } = require('@adobe-commerce/aio-toolkit');
const schema =
type Query {
hello: String
user(id: ID!): User
}
type User {
id: ID!
name: String!
email: String!
};
const resolvers = async ctx => {
const { logger } = ctx;
return {
hello: () => 'Hello, World!',
user: ({ id }) => {
logger.debug(Fetching user: ${id});
return {
id,
name: 'John Doe',
email: 'john@example.com'
};
}
};
};
// Create and export GraphQL endpoint
exports.main = GraphQlAction.execute(schema, resolvers);
`
#### OpenWhisk & OpenWhiskAction
OpenWhisk client for serverless action invocation and integration. OpenWhisk action wrapper with enhanced logging and error handling.
`typescript
const { OpenWhisk, OpenwhiskAction, RuntimeActionResponse } = require('@adobe-commerce/aio-toolkit');
// Invoke another action
const invokeAction = async (params) => {
try {
// Initialize OpenWhisk client
const openwhisk = new Openwhisk(params.API_HOST, params.API_AUTH)
const result = await openwhisk.execute('hello-world', {
name: 'World'
});
console.log('Action result:', result);
return result;
} catch (error) {
console.error('Action invocation failed:', error);
throw error;
}
};
// Simple action with logging
const helloWorldAction = OpenwhiskAction.execute('hello-world', async (params, ctx) => {
const { logger } = ctx;
const { name = 'World' } = params;
logger.info(Greeting request for: ${name});
const message = Hello, ${name}!;
logger.info(Generated greeting: ${message});
return RuntimeActionResponse.success({
message,
timestamp: new Date().toISOString(),
action: 'hello-world'
});
});
// Export for Adobe I/O Runtime
exports.main = helloWorldAction;
`
#### Telemetry
OpenTelemetry integration for enterprise-grade observability with distributed tracing, metrics collection, and structured logging.
Features:
- Automatic telemetry instrumentation for all framework action classes
- Request ID correlation across all log messages (x-adobe-commerce-request-id)action.type
- Action type identification for better filtering ()ctx.telemetry
- Access to current OpenTelemetry span via for custom instrumentationtelemetry.instrument()
- method for wrapping functions with automatic span creationENABLE_TELEMETRY
- Conditional span access based on flag
- Multi-provider support (New Relic implemented, Grafana ready)
- Graceful fallback when telemetry is not configured
- Zero performance impact when disabled (opt-in via feature flags)
Configuration:
Configure telemetry in your app.config.yaml:
`yaml`
runtime-action:
function: actions/runtime-action/index.js
web: 'yes'
runtime: nodejs:22
inputs:
LOG_LEVEL: debug
ENVIRONMENT: $ENVIRONMENT
ENABLE_TELEMETRY: true
NEW_RELIC_TELEMETRY: true
NEW_RELIC_SERVICE_NAME: $NEW_RELIC_SERVICE_NAME
NEW_RELIC_LICENSE_KEY: $NEW_RELIC_LICENSE_KEY
annotations:
require-adobe-auth: true
final: true
Set environment variables in your .env file:
`bash`Telemetry configuration
ENVIRONMENT=production
NEW_RELIC_SERVICE_NAME=my-app-builder-app
NEW_RELIC_LICENSE_KEY=your-license-key-here
NEW_RELIC_URL=https://otlp.nr-data.net:4318 # Optional
Usage Example:
Telemetry is automatically initialized when you use framework action classes:
`javascript
/*
*
*/
const { RuntimeAction, RuntimeActionResponse, HttpMethod } = require("@adobe-commerce/aio-toolkit");
// Custom function with telemetry
const processData = (data, telemetry, logger) => {
const span = telemetry?.getCurrentSpan?.();
if (span) {
span.setAttribute('data.size', data.length);
}
logger.info('Processing data');
return { processed: true };
};
exports.main = RuntimeAction.execute(
'my-action',
[HttpMethod.POST],
['data'],
['Authorization'],
async (params, ctx) => {
const { logger, telemetry } = ctx;
// Logger automatically includes x-adobe-commerce-request-id and action.type
logger.info('Action started');
// Wrap function to create child span
const instrumented = telemetry?.instrument?.('data.process', processData) || processData;
const result = instrumented(params.data, telemetry, logger);
return RuntimeActionResponse.success(result);
}
);
`
Key Features:
- telemetry.getCurrentSpan(params) - Access current span for custom attributes and eventstelemetry.instrument(spanName, fn)
- - Wrap functions to create child spans automatically
- Safe with optional chaining - gracefully degrades when telemetry is disabled
- Logger automatically includes request ID and action type for correlation
What Gets Logged Automatically:
All log messages automatically include:
- x-adobe-commerce-request-id - Extracted from webhook headers for request correlationaction.type
- - Identifies the action type (runtime-action, webhook-action, event-consumer-action, openwhisk-action)
- Action lifecycle events (start, parameters, completion, errors)
- Full stack traces for errors
Viewing in New Relic:
Once configured, you can query your logs in New Relic using these attributes:
`sqlx-adobe-commerce-request-id
-- Filter by request ID
SELECT * FROM Log WHERE = 'request-123'
-- Filter by action type
SELECT * FROM Log WHERE action.type = 'webhook-action'
-- Find errors for a specific request
SELECT * FROM Log WHERE x-adobe-commerce-request-id = 'request-123' AND level = 'error'
-- Group by action type
SELECT count(*) FROM Log FACET action.type SINCE 1 hour ago`
Supported Action Classes:
Telemetry is automatically integrated with all framework action classes:
- RuntimeAction - HTTP request handling with telemetryWebhookAction
- - Webhook processing with telemetryEventConsumerAction
- - Event-driven processing with telemetryOpenwhiskAction
- - OpenWhisk action handling with telemetry
All action classes provide structured logging with automatic request ID and action type tracking.
#### RuntimeApiGatewayService
Flexible Adobe I/O Runtime API Gateway client that accepts bearer tokens for authenticated requests.
`typescript
const { RuntimeApiGatewayService, AdobeAuth } = require('@adobe-commerce/aio-toolkit');
// Step 1: Generate token using AdobeAuth (implement caching in your application)
const token = await AdobeAuth.getToken(
'your-client-id',
'your-client-secret',
'your-technical-account-id',
'your-technical-account-email@example.com',
'your-ims-org-id@AdobeOrg',
['openid', 'AdobeID', 'adobeio_api']
);
// Step 2: Initialize the service with the token
const apiGatewayService = new RuntimeApiGatewayService(
'your-namespace-12345', // Runtime namespace
'your-ims-org-id@AdobeOrg', // IMS Org ID
token, // Bearer token
logger // Optional custom logger
);
// GET request
const data = await apiGatewayService.get('v1/web/my-package/my-action');
console.log('Action response:', data);
// POST request with payload
const result = await apiGatewayService.post('v1/web/my-package/process', {
orderId: 'ORD-123',
action: 'process'
});
// PUT request
const updateResult = await apiGatewayService.put('v1/web/my-package/update', {
id: '123',
status: 'completed'
});
// DELETE request
const deleteResult = await apiGatewayService.delete('v1/web/my-package/remove/123');
`
Key Features:
- Built-in authentication headers
- Support for GET, POST, PUT, DELETE methods
- Flexible endpoint configuration
- Comprehensive error handling
- Accepts pre-generated bearer tokens (implement your own caching strategy)
- CustomLogger integration
💡 Best Practice: Implement token caching at the application level (Redis, Memcached, in-memory) and validate tokens with BearerToken.info() before use to avoid unnecessary token generation calls.
#### FileRepository
File-based storage with CRUD operations for Adobe I/O Runtime applications.
Key Methods:
- save(payload, id?, overwrite?): Saves data with optional ID parameter and overwrite flag. The id parameter takes precedence over payload.id. IDs are automatically sanitized to alphanumeric + underscore characters. Set overwrite: true to replace entire file, or false (default) to merge with existing data.load(id)
- : Loads data by ID with file system timestamps (createdAt, updatedAt)list()
- : Lists all stored records with file system timestampsmetadata(id?)
- : Retrieves file metadata (size, timestamps) without reading content - faster than list() for large datasetsdelete(ids)
- : Deletes records by ID array
New in v1.0.4:
- File System Timestamps: createdAt and updatedAt are now retrieved from actual file system properties instead of being stored in file contentmetadata()
- Metadata Method: New method for efficient file property retrieval without reading file contentsave(payload, id, overwrite)
- Overwrite Flag: Control file update strategy with - merge (default) or replace entire file
Best Practice: Create custom repository classes that extend FileRepository for specific entities.
##### 1. Define Entity Repository
Create a custom repository class extending FileRepository:
`javascript
const { FileRepository } = require("@adobe-commerce/aio-toolkit");
class EntityRepository extends FileRepository {
/**
* @constructor
*/
constructor() {
super("/toolkit-demo/entity");
}
}
module.exports = EntityRepository;
`
##### 2. List Action
Retrieve all entities from the repository:
`javascript
const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");
exports.main = RuntimeAction.execute(
"entity-list",
[HttpMethod.POST],
[],
['Authorization'],
async (params) => {
const entityRepository = new EntityRepository();
return RuntimeActionResponse.success(await entityRepository.list());
}
);
`
##### 3. Load Action
Load a specific entity by ID:
`javascript
const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");
exports.main = RuntimeAction.execute(
"entity-load",
[HttpMethod.POST],
['id'],
['Authorization'],
async (params) => {
const entityRepository = new EntityRepository();
return RuntimeActionResponse.success(await entityRepository.load(params.id));
}
);
`
##### 4. Save Action
Save entity data with flexible ID handling using the new optional ID parameter:
`javascript
const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");
const requiredParams = [
"name",
"status"
];
exports.main = RuntimeAction.execute(
"entity-save",
[HttpMethod.POST],
requiredParams,
['Authorization'],
async (params) => {
const entityRepository = new EntityRepository();
// Build payload with required fields
let payload = {};
for (const fieldName in requiredParams) {
payload[requiredParams[fieldName]] = params[requiredParams[fieldName]];
}
// Extract ID parameter for prioritized handling
const explicitId = params.id || params.customId || null;
// Save with optional ID parameter - it takes precedence over payload.id
const savedId = await entityRepository.save(payload, explicitId);
return RuntimeActionResponse.success({
id: savedId,
message: 'Entity saved successfully'
});
}
);
`
##### 5. Save with Overwrite Flag
Control file update strategy with the overwrite parameter:
`javascript
const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");
exports.main = RuntimeAction.execute(
"entity-save-overwrite",
[HttpMethod.POST],
['name', 'status'],
['Authorization'],
async (params) => {
const entityRepository = new EntityRepository();
const payload = {
name: params.name,
status: params.status
};
// Replace entire file instead of merging
const savedId = await entityRepository.save(payload, params.id, true);
return RuntimeActionResponse.success({
id: savedId,
message: 'Entity replaced successfully'
});
}
);
`
##### 6. Metadata Action
Retrieve file metadata without reading content (faster for large datasets):
`javascript
const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");
exports.main = RuntimeAction.execute(
"entity-metadata",
[HttpMethod.POST],
[],
['Authorization'],
async (params) => {
const entityRepository = new EntityRepository();
// Get metadata for all files
const allMetadata = await entityRepository.metadata();
// Or get metadata for specific file
const singleMetadata = params.id
? await entityRepository.metadata(params.id)
: null;
return RuntimeActionResponse.success({
all: allMetadata,
single: singleMetadata
});
}
);
`
##### 7. Delete Action
Delete entities by providing an array of IDs:
`javascript
const { HttpMethod, RuntimeAction, RuntimeActionResponse } = require("@adobe-commerce/aio-toolkit");
const EntityRepository = require("@lib/EntityRepository");
exports.main = RuntimeAction.execute(
"entity-delete",
[HttpMethod.POST],
['ids'],
['Authorization'],
async (params) => {
const entityRepository = new EntityRepository();
return RuntimeActionResponse.success(await entityRepository.delete(params.ids));
}
);
`
This approach provides:
- Separation of concerns: Each CRUD operation has its own action file
- Reusable repository: Custom repository can be shared across actions
- Proper validation: Required parameters and headers are enforced
- Consistent responses: All actions use RuntimeActionResponse for standardized output
- Flexible ID management: Support for explicit IDs, payload IDs, and auto-generation
- Automatic sanitization: IDs are cleaned to ensure file system compatibility
Adobe Commerce API integration and authentication
#### AdobeAuth
Adobe IMS authentication and token management.
`typescript
const { AdobeAuth } = require('@adobe-commerce/aio-toolkit');
// Get authentication token
const token = await AdobeAuth.getToken(
'your-client-id',
'your-client-secret',
'your-technical-account-id',
'your-technical-account-email',
'your-ims-org-id',
['AdobeID', 'openid', 'adobeio_api'] // Scopes
);
console.log('Authentication token:', token);
`
#### AdobeCommerceClient
HTTP client for Adobe Commerce API integration with multiple authentication methods.
OAuth 1.0a Authentication
`typescript
const { AdobeCommerceClient, Oauth1aConnection } = require('@adobe-commerce/aio-toolkit');
const connection = new Oauth1aConnection(
'consumer-key',
'consumer-secret',
'access-token',
'access-token-secret',
logger // Optional custom logger
);
// Create client
const client = new AdobeCommerceClient('https://your-commerce-store.com/rest', connection);
// Make API calls
const products = await client.get('V1/products');
const newProduct = await client.post('V1/products', {}, productData);
`
IMS (Identity Management System) Authentication
`typescript
const { AdobeCommerceClient, ImsConnection, AdobeAuth, BearerToken } = require('@adobe-commerce/aio-toolkit');
// Step 1: Generate token using AdobeAuth (implement caching in your application)
// Check your application cache first
let token = await yourAppCache.get('commerce_ims_token');
if (!token || !BearerToken.info(token).isValid) {
// Generate new token
token = await AdobeAuth.getToken(
'client-id',
'client-secret',
'technical-account-id',
'technical-account-email',
'ims-org-id@AdobeOrg',
['AdobeID', 'openid', 'adobeio_api']
);
// Store in your application cache with appropriate TTL
const tokenInfo = BearerToken.info(token);
if (tokenInfo.isValid && tokenInfo.timeUntilExpiry) {
const ttl = Math.floor(tokenInfo.timeUntilExpiry / 1000) - 600; // 10-min buffer
await yourAppCache.set('commerce_ims_token', token, ttl);
}
}
// Step 2: Create connection with token
const connection = new ImsConnection(token, logger); // Optional logger
// Step 3: Create client with IMS authentication
const client = new AdobeCommerceClient('https://your-commerce-store.com', connection);
// Make API calls
const products = await client.get('V1/products');
const newProduct = await client.post('V1/products', {}, productData);
`
> 💡 Best Practice: Implement token caching at the application level (Redis, Memcached, in-memory) to avoid unnecessary token generation calls.
⚠️ DEPRECATED: ImsToken Class
> This class is deprecated due to built-in caching mechanism issues:
> - Relies on Adobe I/O Runtime State API which has reliability issues
> - State API may not be available in all runtime environments
> - Hard-coded TTL buffers and minimum TTL are inflexible
> - Limited control over caching strategy
>
> Migration Required: Use AdobeAuth directly and implement caching at the application level.
`typescript
// ❌ OLD (Deprecated) - ImsToken with built-in caching
const { ImsToken } = require('@adobe-commerce/aio-toolkit');
const imsToken = new ImsToken(
'client-id',
'client-secret',
'technical-account-id',
'technical-account-email',
'ims-org-id@AdobeOrg',
['AdobeID', 'openid', 'adobeio_api'],
logger // optional
);
const token = await imsToken.execute();
`
`typescript
// ✅ NEW (Recommended) - AdobeAuth with application-level caching
const { AdobeAuth, BearerToken } = require('@adobe-commerce/aio-toolkit');
// Check your application cache first (Redis, Memcached, in-memory, etc.)
let token = await yourAppCache.get('ims_token');
// Validate cached token
if (!token || !BearerToken.info(token).isValid) {
// Generate new token using AdobeAuth
token = await AdobeAuth.getToken(
'client-id',
'client-secret',
'technical-account-id',
'technical-account-email',
'ims-org-id@AdobeOrg',
['AdobeID', 'openid', 'adobeio_api']
);
// Store in your application cache with appropriate TTL
const tokenInfo = BearerToken.info(token);
if (tokenInfo.isValid && tokenInfo.timeUntilExpiry) {
const ttl = Math.floor(tokenInfo.timeUntilExpiry / 1000) - 600; // 10-min buffer
await yourAppCache.set('ims_token', token, ttl);
}
}
// Use the token
console.log('Token:', token);
`
Why Migrate?
- ✅ Full control over caching strategy (Redis, Memcached, in-memory, etc.)
- ✅ Better error handling and retry logic
- ✅ Works in any environment (not just Adobe I/O Runtime)
- ✅ Easier to test and mock
- ✅ Flexible TTL management based on your needs
Basic Authentication
`typescript
const { AdobeCommerceClient, BasicAuthConnection } = require('@adobe-commerce/aio-toolkit');
const connection = new BasicAuthConnection(
'username',
'password',
logger // Optional custom logger
);
// Create client
const client = new AdobeCommerceClient('https://your-commerce-store.com/rest', connection);
// Make API calls
const products = await client.get('V1/products');
`
#### ShippingCarrier
Fluent builder for creating custom shipping carriers for Adobe Commerce webhook extensibility.
`typescript
const {
ShippingCarrier,
ShippingCarrierResponse
} = require('@adobe-commerce/aio-toolkit');
// Create a custom shipping carrier with methods
const carrier = new ShippingCarrier('fedex', (carrier) => {
carrier
.setTitle('FedEx Express')
.setStores(['default', 'store1'])
.setCountries(['US', 'CA', 'MX'])
.setSortOrder(10)
.setActive(true)
.setTrackingAvailable(true)
.setShippingLabelsAvailable(true)
.addMethod('standard', (method) => {
method
.setMethodTitle('Standard Shipping')
.setPrice(9.99)
.setCost(5.00)
.addAdditionalData('delivery_time', '3-5 business days')
.addAdditionalData('tracking_available', true);
})
.addMethod('express', (method) => {
method
.setMethodTitle('Express Shipping')
.setPrice(19.99)
.setCost(12.00)
.addAdditionalData('delivery_time', '1-2 business days');
})
.removeMethod('overnight'); // Remove a method
});
// Get carrier configuration
const carrierData = carrier.getData();
console.log(carrierData);
// Access added and removed methods
const addedMethods = carrier.getAddedMethods(); // ['standard', 'express']
const removedMethods = carrier.getRemovedMethods(); // ['overnight']
// Generate webhook response operations
const response = new ShippingCarrierResponse(carrier);
const operations = response.generate();
return operations; // Use in webhook action
// Update carrier data (code is immutable)
carrier.setData({
code: 'ups', // This will be ignored - code remains 'fedex'
title: 'Demo Postal Service',
stores: ['default'],
countries: ['US', 'CA'],
sort_order: 10,
active: true,
tracking_available: true,
shipping_labels_available: true
});
// Code property is immutable - create new instance if you need different code
const newCarrier = new ShippingCarrier('ups', (c) => {
c.addMethod('ground', (m) => {
m.setMethodTitle('UPS Ground').setPrice(12.99);
});
});
`
Key Features:
- Builder pattern with method chaining
- Validation for carrier and method codes (alphanumeric and underscores only)
- Add and remove shipping methods dynamically
- Configure carrier properties (title, stores, countries, sort order, etc.)
- Immutable code property - prevents accidental carrier identity changes
- Public getter methods: getAddedMethods() and getRemovedMethods()
- Generate webhook response operations
- Type-safe TypeScript interfaces
Validation Rules:
- Carrier and method codes must contain only alphanumeric characters and underscores
- No spaces, hyphens, dots, or special characters allowed
- Empty or whitespace-only codes throw errors
- Code property cannot be changed after initialization
#### OnboardCommerce
Complete Adobe Commerce I/O Events configuration orchestration with automated provider setup and event subscription management.
`typescript
const {
OnboardCommerce,
AdobeCommerceClient,
ImsConnection
} = require('@adobe-commerce/aio-toolkit');
const Core = require('@adobe/aio-sdk').Core;
// Generate token using AdobeAuth (implement caching in your application)
const token = await AdobeAuth.getToken(
'client-id',
'client-secret',
'technical-account-id',
'technical-account-email',
'ims-org-id@AdobeOrg',
['AdobeID', 'openid', 'adobeio_api']
);
// Initialize Adobe Commerce client
const connection = new ImsConnection(token, logger);
const adobeCommerceClient = new AdobeCommerceClient(
'https://your-commerce-store.com',
connection
);
// Initialize logger
const logger = Core.Logger('onboard-client', {
level: 'debug'
});
// Initialize OnboardCommerce
const onboardCommerce = new OnboardCommerce(
adobeCommerceClient,
process.env.COMMERCE_ADOBE_IO_EVENTS_MERCHANT_ID || '',
process.env.COMMERCE_ADOBE_IO_EVENTS_ENVIRONMENT_ID || '',
logger,
false // isPaaS: set to true for PaaS instances (defaults to false)
);
// Define commerce provider
const commerceProvider = {
raw: {
id: 'commerce-provider-id',
label: 'Commerce Events Provider',
description: 'Provider for Adobe Commerce events',
docsUrl: 'https://developer.adobe.com/commerce/events'
}
};
// Define workspace configuration
const workspaceConfig = {
project: {
id: process.env.ADOBE_PROJECT_ID,
name: 'My Commerce Project',
title: 'Commerce Integration'
},
workspace: {
id: process.env.ADOBE_WORKSPACE_ID,
name: 'Production'
}
};
// Define commerce events configuration
const commerceEventsConfig = [
{
event: {
name: 'com.adobe.commerce.observer.catalog_product_save_after',
label: 'Product Saved',
description: 'Triggered when a product is saved'
}
},
{
event: {
name: 'com.adobe.commerce.observer.sales_order_save_after',
label: 'Order Saved',
description: 'Triggered when an order is saved'
}
}
];
// Execute configuration
const result = await onboardCommerce.process(
commerceProvider.raw,
workspaceConfig,
commerceEventsConfig
);
if (result.success) {
console.log('✅ Commerce events configured successfully');
console.log(Provider: ${result.provider?.label});`
} else {
console.error('❌ Configuration failed:', result.error);
}
Key Features:
- Automated provider configuration with validation
- Event subscription management with duplicate detection
- Intelligent event metadata validation against supported Commerce events
- PaaS support for Adobe Commerce Cloud instances with native event handling
- Structured logging with prefixes: [START], [CREATE], [SKIP], [ERROR], [IMPORTANT]
- Comprehensive summary reporting with event subscription statistics
- Integration with Adobe Commerce API for event discovery
- Automatic post-subscription CLI instructions for PaaS instances
- 100% test coverage
PaaS Support:
For Adobe Commerce PaaS (Platform as a Service) instances, enable PaaS mode to support native events:
`typescript
// Initialize OnboardCommerce for PaaS instances
const onboardCommercePaaS = new OnboardCommerce(
adobeCommerceClient,
merchantId,
environmentId,
logger,
true // Enable PaaS mode
);
// Define native PaaS events (observer., plugin.)
const paasEventsConfig = [
{
event: {
name: 'observer.catalog_product_save_after',
label: 'Product Saved',
description: 'Native observer event'
}
},
{
event: {
name: 'plugin.magento.catalog.model.product.save',
label: 'Product Save Plugin',
description: 'Native plugin event'
}
}
];
// Process PaaS events
const result = await onboardCommercePaaS.process(
commerceProvider.raw,
workspaceConfig,
paasEventsConfig
);
// PaaS mode automatically:
// - Skips supported events validation for better performance
// - Handles native events without the 'parent' field
// - Displays post-subscription Magento CLI commands
`
After successful event subscription on PaaS, the system displays:
``
[IMPORTANT] ⚠️ Post-Subscription Steps for PaaS:
[IMPORTANT] 1. Run: bin/magento events:generate:module to generate module after successful event subscription
[IMPORTANT] 2. Run: bin/magento setup:upgrade && bin/magento setup:di:compile && bin/magento cache:flush to install the generated module
Adobe Commerce Admin UI extension and user experience tools
#### AdminUiSdk
Create and manage Adobe Commerce Admin UI extensions with menu items, sections, and page configurations.
`typescript
const { AdminUiSdk } = require('@adobe-commerce/aio-toolkit');
const sdk = new AdminUiSdk('dataMappingTool');
// Add menu section with external parent
sdk.addMenuSection(
'dataMappingTool::checkout_integration',
'Checkout Integration',
100,
'Magento_Backend::system'
);
// Add menu item
sdk.addMenuItem(
'dataMappingTool::application',
'Application',
1,
'dataMappingTool::checkout_integration'
);
// Set page title and get registration
sdk.addPage('Data Mapping Tool Dashboard');
const registration = sdk.getRegistration();
`
External API integration and utility functions
#### RestClient
HTTP client for external API integration with support for various payload types.
Basic Usage
`typescript
const { RestClient } = require('@adobe-commerce/aio-toolkit');
const client = new RestClient();
// GET request
const response = await client.get('https://api.example.com/data', {
'Authorization': 'Bearer token'
});
`
JSON Payloads (default)
`typescript`
// POST with JSON (automatic Content-Type: application/json)
const jsonData = { name: 'Product', price: 99.99 };
const response = await client.post('https://api.example.com/products', {
'Authorization': 'Bearer token'
}, jsonData);
Form-Encoded Requests
`typescript
// URLSearchParams for form-encoded data (automatic Content-Type: application/x-www-form-urlencoded)
const formData = new URLSearchParams({
grant_type: 'client_credentials',
client_id: 'your-client-id',
client_secret: 'your-client-secret'
});
const tokenResponse = await client.post('https://auth.example.com/token', {
Accept: 'application/json'
}, formData);
`
File Upload
`typescript
// FormData for file uploads (Content-Type boundary handled automatically)
const uploadData = new FormData();
uploadData.append('file', fileBuffer, 'document.pdf');
uploadData.append('description', 'Important document');
const uploadResponse = await client.post('https://api.example.com/upload', {
'Authorization': 'Bearer token'
}, uploadData);
`
Text/XML Payloads
`typescript`
// String payloads with custom content type
const xmlData = '
const xmlResponse = await client.post('https://api.example.com/orders', {
'Authorization': 'Bearer token',
'Content-Type': 'application/xml'
}, xmlData);
#### BearerToken
Bearer token extraction and JWT analysis utility. Supports both standard HTTP headers and OpenWhisk format for maximum portability.
Extract from Standard HTTP Headers
`typescript
const { BearerToken } = require('@adobe-commerce/aio-toolkit');
const headers = { authorization: 'Bearer abc123token' };
const tokenInfo = BearerToken.extract(headers);
console.log(tokenInfo);
// Output: {
// token: 'abc123token',
// tokenLength: 11,
// isValid: true,
// expiry: '2024-01-02T12:00:00.000Z',
// timeUntilExpiry: 86400000
// }
`
Extract from OpenWhisk Params (Backward Compatibility)
`typescript`
const params = { __ow_headers: { authorization: 'Bearer abc123token' } };
const owTokenInfo = BearerToken.extract(params);
console.log(owTokenInfo); // Same output format as above
Direct Token Analysis
`typescriptToken expires at: ${directInfo.expiry}
const directInfo = BearerToken.info('jwt-token-string');
if (directInfo.isValid) {
console.log();Time until expiry: ${directInfo.timeUntilExpiry}ms
console.log();`
} else {
console.log('Token is invalid or expired');
}
Methods:
- extract(headersOrParams) - Extracts Bearer token from headers or OpenWhisk paramsinfo(token)
- - Analyzes token string and returns validation/expiry details
#### InfiniteLoopBreaker
Detect and prevent infinite loops in event-driven applications.
`typescript
const { InfiniteLoopBreaker } = require('@adobe-commerce/aio-toolkit');
// Basic infinite loop detection
const isLoop = await InfiniteLoopBreaker.isInfiniteLoop({
keyFn: 'order-processing-key',
fingerprintFn: orderData,
eventTypes: ['order.created', 'order.updated'],
event: 'order.created'
});
if (isLoop) {
console.log('Infinite loop detected, skipping processing');
return;
}
// Process the event
await processOrderEvent(orderData);
// Store fingerprint to prevent future loops
await InfiniteLoopBreaker.storeFingerPrint(
'order-processing-key',
orderData,
300 // 5 minutes TTL
);
`
#### OnboardIOEvents (formerly OnboardEvents)
Complete onboarding orchestration for Adobe I/O Events (providers, metadata, and registrations).
> Note: OnboardEvents is deprecated and will be removed in v2.0.0. Please use OnboardIOEvents instead.
`typescript
const { OnboardIOEvents } = require('@adobe-commerce/aio-toolkit');
// ✅ Recommended - Use OnboardIOEvents
const onboardEvents = new OnboardIOEvents(
'My E-commerce Store',
process.env.ADOBE_CONSUMER_ID!,
process.env.ADOBE_PROJECT_ID!,
process.env.ADOBE_WORKSPACE_ID!,
process.env.ADOBE_API_KEY!,
process.env.ADOBE_ACCESS_TOKEN!
);
const basicOnboardingExample = async () => {
const input = {
providers: [
{
key: 'ecommerce-provider',
label: 'E-commerce Events Provider',
description: 'Provider for e-commerce platform events',
docsUrl: 'https://docs.mystore.com/events',
registrations: [
{
key: 'order-events',
label: 'Order Events Registration',
description: 'Registration for order-related events',
events: [
{
eventCode: 'com.mystore.order.placed',
runtimeAction: 'mystore/order-placed-handler',
deliveryType: 'webhook',
sampleEventTemplate: {
orderId: 'ord_123456',
customerId: 'cust_789',
totalAmount: 99.99,
currency: 'USD',
status: 'placed',
timestamp: '2023-01-01T12:00:00Z'
}
},
{
eventCode: 'com.mystore.order.shipped',
runtimeAction: 'mystore/order-shipped-handler',
deliveryType: 'journal',
sampleEventTemplate: {
orderId: 'ord_123456',
trackingNumber: 'TN123456789',
carrier: 'UPS',
shippedAt: '2023-01-02T10:00:00Z'
}
}
]
}
]
}
]
};
try {
const result = await onboardEvents.process(input);
console.log('Onboarding completed successfully!');
console.log(Providers: ${result.createdProviders.length} processed);Events: ${result.createdEvents.length} processed
console.log();Registrations: ${result.createdRegistrations.length} processed
console.log();✅ Created provider: ${provider.provider.label}
// Check results
result.createdProviders.forEach(provider => {
if (provider.created) {
console.log();⏭️ Skipped existing provider: ${provider.provider.label}
} else if (provider.skipped) {
console.log();❌ Failed to create provider: ${provider.error}
} else {
console.log();
}
});
return result;
} catch (error) {
console.error('Onboarding failed:', error.message);
return null;
}
};
basicOnboardingExample();
`
Adobe I/O Events management and orchestration
#### EventMetadataManager
Manage event metadata for Adobe I/O Events providers.
`typescript
const { EventMetadataManager } = require('@adobe-commerce/aio-toolkit');
const manager = new EventMetadataManager({
apiKey: 'your-api-key',
accessToken: 'your-access-token',
consumerId: 'your-consumer-id',
projectId: 'your-project-id',
workspaceId: 'your-workspace-id'
});
// Create event metadata
const eventMetadata = await manager.create({
providerId: 'your-provider-id',
eventCode: 'commerce.order.created',
label: 'Order Created',
description: 'Triggered when a new order is created in the commerce system',
sampleEventTemplate: {
orderId: 'ORD-123456',
customerId: 'CUST-789',
amount: 199.99,
currency: 'USD',
items: [
{
sku: 'PRODUCT-001',
name: 'Premium Widget',
quantity: 2,
price: 99.99
}
],
timestamp: '2023-12-01T10:30:00Z'
}
});
// Get event metadata
const metadata = await manager.get('your-provider-id', 'commerce.order.created');
// List all event metadata for a provider
const allMetadata = await manager.list('your-provider-id');
// Delete specific event metadata
await manager.delete('your-provider-id', 'commerce.order.created');
// Delete all event metadata for a provider
await manager.delete('your-provider-id');
`
#### ProviderManager
Manage event providers for Adobe I/O Events.
`typescript
const { ProviderManager } = require('@adobe-commerce/aio-toolkit');
const manager = new ProviderManager({
apiKey: 'your-api-key',
accessToken: 'your-access-token',
consumerId: 'your-consumer-id',
projectId: 'your-project-id',
workspaceId: 'your-workspace-id'
});
// Create a new provider
const provider = await manager.create({
label: 'E-commerce Events Provider',
description: 'Provides events from our e-commerce platform including orders, customers, products, and inventory updates',
docsUrl: 'https://docs.mystore.com/events-api',
providerMetadata: {
region: 'us-east-1',
environment: 'production',
version: '2.1.0'
}
});
// Get provider details
const existingProvider = await manager.get('your-provider-id', {
eventmetadata: true // Include event metadata in response
});
// List all providers
const providers = await manager.list({
eventmetadata: true, // Include event metadata
providerMetadataIds: ['provider-1', 'provider-2'] // Filter by specific providers
});
// List providers with advanced filtering
const filteredProviders = await manager.list({
instanceId: 'your-instance-id',
providerMetadataId: 'specific-provider-metadata-id'
});
// Delete provider
await manager.delete('your-provider-id');
`
#### RegistrationManager
Manage event registrations and subscriptions for Adobe I/O Events.
`typescript
const { RegistrationManager } = require('@adobe-commerce/aio-toolkit');
const manager = new RegistrationManager({
apiKey: 'your-api-key',
accessToken: 'your-access-token',
consumerId: 'your-consumer-id',
projectId: 'your-project-id',
workspaceId: 'your-workspace-id'
});
// Create event registration
const registration = await manager.create({
name: 'Order Processing Registration',
description: 'Handles order-related events',
delivery: {
type: 'webhook',
url: 'https://your-app.com/webhook/orders'
},
eventsOfInterest: [
{
providerId: 'your-provider-id',
eventCode: 'commerce.order.created'
},
{
providerId: 'your-provider-id',
eventCode: 'commerce.order.updated'
}
]
});
// Get registration details
const existingRegistration = await manager.get('your-registration-id');
// List all registrations
const registrations = await manager.list();
// Delete registration
await manager.delete('your-registration-id');
`
Common environment variables used across components:
`bash``Adobe commerce credentials
COMMERCE_BASE_URL=commerce-base-url
COMMERCE_CONSUMER_KEY=commerce-consumer-key
COMMERCE_CONSUMER_SECRET=commerce-consumer-secret
COMMERCE_ACCESS_TOKEN=commerce-access-token
COMMERCE_ACCESS_TOKEN_SECRET=commerce-access-token-secretEnvironment from DIST file
OAUTH_BASE_URL=https://ims-na1.adobelogin.com/ims/token/
IO_MANAGEMENT_BASE_URL=https://api.adobe.io/events/OAuth configs
The following values can be copied from the Credential details page in AppBuilder under Organization > Project > Workspace > OAuth Server-to-Server
OAUTH_CLIENT_ID=client-id
OAUTH_CLIENT_SECRET=client-secret
OAUTH_TECHNICAL_ACCOUNT_ID=technical-account-id
OAUTH_TECHNICAL_ACCOUNT_EMAIL=technical-account-email
OAUTH_ORG_ID=org-id
OAUTH_SCOPES=scopesOAUTH_HOST=
Workspace configs
The following values can be copied from the JSON downloadable in AppBuilder from Organization > Project > Workspace
IO_CONSUMER corresponds to project.org.id
IO_PROJECT_ID corresponds to project.id
IO_WORKSPACE_ID corresponds to project.workspace.id
IO_CONSUMER_ID=consumer-id
IO_PROJECT_ID=project-id
IO_WORKSPACE_ID=workspace-id
See the LICENSE file (in package) for license rights and limitations.