A TypeScript template for Adobe App Builder that demonstrates how to build Adobe Commerce applications with type safety and modern development practices.
This template provides a TypeScript-based foundation for building Adobe App Builder applications. While the official Adobe Commerce Integration Starter Kit provides comprehensive JavaScript implementations for multi-provider integrations (ERPs, CRMs, PIMs), this template focuses on defining TypeScript configuration that works with Adobe App Builder, along with initial types and utility functions for:
- Adobe IO Events: Sending events through Adobe IO Events
- Adobe I/O lib-state: State management utilities
- Adobe I/O lib-files: File storage utilities
- Adobe Commerce Client: Works with both PaaS and SaaS Adobe Commerce instances
- Type Safety: Full TypeScript support with strict type checking
-
Clone the template:
git clone <repository-url> cd app-builder-typescript-template
-
Install dependencies:
npm install
-
Connect to Adobe App Builder:
This step will connect your starter kit project to the App Builder project you created earlier. Ensure to select the proper Organization > Project > Workspace with the following commands:
aio login aio console org select aio console project select aio console workspace select
Sync your local application with the App Builder project using the following command:
aio app use # Choose the option 'm' (merge)
-
Configure environment:
cp env.sample .env # Edit .env with your Adobe Commerce credentials
-
Build the project:
npm run build
For development, you can use the watch mode to automatically rebuild on file changes:
npm run watch
-
Run tests:
npm test
-
Deploy to Adobe App Builder:
aio app deploy
app-builder-typescript-template/
├── actions-src/ # TypeScript source code
│ ├── example-1/ # Example 1: Direct function definition
│ │ └── process/
│ │ ├── index.ts
│ │ └── types/
│ ├── example-2/ # Example 2: Imported configuration
│ │ ├── action/
│ │ │ ├── index.ts
│ │ │ └── types/
│ │ └── actions.config.yaml
│ ├── logger.ts # Logging utilities
│ ├── openwhisk.ts # OpenWhisk utilities
│ ├── responses.ts # Response helpers
│ ├── types/ # TypeScript type definitions
│ ├── utils.ts # Utility functions
│ └── utils/ # Utility modules
│ ├── adobe-commerce/ # Adobe Commerce integration
│ │ ├── adobe-commerce-client.ts
│ │ └── types/
│ ├── io-auth/ # Adobe authentication
│ │ └── adobe-auth.ts
│ ├── io-events/ # Adobe IO Events
│ │ ├── adobe-events-api.ts
│ │ └── naming.ts
│ ├── lib-files/ # File storage utilities
│ │ └── storage-client.ts
│ └── lib-state/ # State management utilities
│ └── state-client.ts
├── actions/ # Compiled JavaScript (generated)
├── packages/ # Local packages
│ └── commerce-sdk-auth/ # Adobe Commerce SDK authentication
├── test/ # Test files
├── app.config.yaml # Main App Builder configuration
├── package.json # Dependencies and scripts
├── tsconfig.json # TypeScript configuration
└── eslint.config.mjs # ESLint configuration
Unlike typical Adobe App Builder projects where source code is directly in the actions/
directory, this template uses a TypeScript workflow:
- Source code:
actions-src/
- Contains all TypeScript files - Compiled output:
actions/
- Contains compiled JavaScript files (generated by build process) - Adobe App Builder: Only uses the
actions/
directory for deployment
tsconfig.json
: Main configuration with CommonJS module systemtsconfig.cjs.json
: CommonJS-specific configurationtsconfig.es.json
: ES modules configurationtsconfig.types.json
: Type declaration generation
The build process compiles TypeScript from actions-src/
to JavaScript in actions/
, ensuring Adobe App Builder can deploy the compiled code.
@adobe/aio-sdk
: Adobe I/O SDK for App Builder@adobe/aio-lib-state
: State management for serverless functions@adobe/aio-lib-ims
: Adobe Identity Management System@adobe/commerce-sdk-auth
: Adobe Commerce authentication utilitiesaxios
: HTTP client for API requestsoauth-1.0a
: OAuth 1.0a authenticationopenwhisk
: OpenWhisk runtime utilitiescloudevents
: CloudEvents specification implementationnode-fetch
: Fetch API for Node.jsuuid
: UUID generation
typescript
: TypeScript compilerjest
: Testing frameworkeslint
: Code linting@typescript-eslint
: TypeScript ESLint rulests-jest
: TypeScript support for Jestts-node
: TypeScript execution for Node.js
In app.config.yaml
, functions are defined directly with paths pointing to the compiled actions/
directory:
example-1:
license: Apache-2.0
actions:
process:
function: actions/example-1/process/index.js # Points to compiled JS
web: "yes"
runtime: nodejs:20
inputs:
LOG_LEVEL: debug
COMMERCE_BASE_URL: $COMMERCE_BASE_URL
# ... other inputs
For complex configurations, you can import separate config files:
example-2:
license: Apache-2.0
actions:
$include: ./actions-src/example-2/actions.config.yaml # Import from source
In actions-src/example-2/actions.config.yaml
:
process:
function: ../../actions/example-2/action/index.js # Points to compiled JS
web: "yes"
runtime: nodejs:20
inputs:
LOG_LEVEL: debug
# ... other inputs
Important: Function paths in configuration files must always point to the actions/
directory (compiled JavaScript), not actions-src/
(TypeScript source).
The template includes a comprehensive Adobe Commerce client that supports:
- OAuth 1.0a: Traditional Adobe Commerce API authentication
- IMS (Identity Management System): Modern Adobe authentication
import { AdobeCommerceClient } from "../utils/adobe-commerce/adobe-commerce-client";
export async function main(params: ProcessFunctionParams): Promise<AppResponse> {
const storeConfig = {
storeCode: "us",
storeUrl: "https://us.example.com",
};
const client = AdobeCommerceClient.create(params, storeConfig);
// Make API calls
const products = await client.get("/rest/V1/products");
return actionSuccessResponse("Operation completed");
}
The template includes comprehensive Adobe IO Events support for publishing events:
import { EventCode, ProviderKey, publishEvent } from "../../utils/io-events/adobe-events-api";
export async function main(params: ProcessFunctionParams): Promise<AppResponse> {
// Publish an event
await publishEvent(
params,
{
productId: params.data.id,
productName: params.data.name,
},
ProviderKey.COMMERCE,
EventCode.PRODUCT_CREATED
);
return actionSuccessResponse("Event published successfully");
}
EventCode.PRODUCT_CREATED
: Product creation events- Additional event codes can be added as needed
ProviderKey.COMMERCE
: Adobe Commerce providerProviderKey.BACKOFFICE
: Backoffice provider
The template includes state management utilities using Adobe I/O lib-state:
import { getStateClient } from "../../utils/lib-state/state-client";
export async function main(params: ProcessFunctionParams): Promise<AppResponse> {
// Use lib state for temporary storage
const stateClient = await getStateClient();
await stateClient.put("key", "value", {
ttl: 1000 * 60 * 60 * 24, // 24 hours
});
// Retrieve data
const value = await stateClient.get("key");
return actionSuccessResponse("State operation completed");
}
The template includes file storage utilities using Adobe I/O lib-files:
import { getStorageClient } from "../../utils/lib-files/storage-client";
export async function main(params: ProcessFunctionParams): Promise<AppResponse> {
// Use lib files for permanent storage
const filesClient = await getStorageClient();
// Write file
await filesClient.write("path/to/file.txt", "content");
// Read file
const content = await filesClient.read("path/to/file.txt");
return actionSuccessResponse("File operation completed");
}
Here's a complete example showing all features together:
import { initializeLogger, logError } from "../../logger";
import { actionErrorResponse, actionSuccessResponse } from "../../responses";
import { getStateClient } from "../../utils/lib-state/state-client";
import { getStorageClient } from "../../utils/lib-files/storage-client";
import { AppResponse, HttpStatus } from "../../types/request";
import { AdobeCommerceClient } from "../../utils/adobe-commerce/adobe-commerce-client";
import { EventCode, ProviderKey, publishEvent } from "../../utils/io-events/adobe-events-api";
import { Product } from "./types/product";
import { ProcessFunctionParams } from "./types/request";
export async function main(params: ProcessFunctionParams): Promise<AppResponse> {
initializeLogger("process-function");
try {
validateProduct(params.data);
const storeConfig = { storeCode: "us", storeUrl: "https://us.example.com" };
const client = AdobeCommerceClient.create(params, storeConfig);
// Make Adobe Commerce API calls
const products = await client.get("/rest/V1/products");
// Publish event
await publishEvent(
params,
{
productId: params.data.id,
productName: params.data.name,
},
ProviderKey.COMMERCE,
EventCode.PRODUCT_CREATED
);
// Use lib state for temporary storage
const stateClient = await getStateClient();
await stateClient.put("key", "value", {
ttl: 1000 * 60 * 60 * 24, // 24 hours
});
// Use lib files for permanent storage
const filesClient = await getStorageClient();
await filesClient.write("path", "content");
return actionSuccessResponse("Product review summarization completed");
} catch (error: unknown) {
const errorMessage = error instanceof Error ? error.message : "Unknown error";
logError(`Server error: ${errorMessage}`);
return actionErrorResponse(HttpStatus.INTERNAL_ERROR, `Request failed: ${errorMessage}`);
}
}
function validateProduct(product: Product): void {
if (!product.id) {
throw new Error("Product ID is required");
}
}
# Build TypeScript to JavaScript
npm run build
# Watch for changes and rebuild
npm run watch
# Run tests
npm test
# Lint code
npm run lint
# Fix linting issues
npm run lint:fix
- Write TypeScript in
actions-src/
- Build with
npm run build
(or usenpm run watch
for development) - Test with
npm test
- Deploy - Adobe App Builder uses the compiled JavaScript in
actions/
The template expects these environment variables for Adobe Commerce integration:
COMMERCE_BASE_URL
: Adobe Commerce instance URLCOMMERCE_CONSUMER_KEY
: OAuth consumer keyCOMMERCE_CONSUMER_SECRET
: OAuth consumer secretCOMMERCE_ACCESS_TOKEN
: OAuth access tokenCOMMERCE_ACCESS_TOKEN_SECRET
: OAuth access token secret
OAUTH_CLIENT_ID
: Adobe IMS client IDOAUTH_CLIENT_SECRET
: Adobe IMS client secretOAUTH_SCOPES
: Required scopesOAUTH_HOST
: IMS host URL
OAUTH_ORG_ID
: Adobe organization IDIO_MANAGEMENT_BASE_URL
: IO Events management base URLIO_CONSUMER_ID
: IO Events consumer IDIO_PROJECT_ID
: IO Events project IDIO_WORKSPACE_ID
: IO Events workspace ID
The template includes Jest for testing with TypeScript support:
// test/adobe-commerce-client.test.ts
import { AdobeCommerceClient } from "../actions-src/utils/adobe-commerce/adobe-commerce-client";
describe("AdobeCommerceClient", () => {
test("should create client with OAuth 1.0a", () => {
// Test implementation
});
});