Jigar KarangiyaJigar Karangiya
Articlebeginner50 minAdobe CommerceApp BuilderREST APIOAuthExtensibility

Call Adobe Commerce REST from an App Builder action

· 7 min read

Lesson 1 left you with a deployed Hello World app on Adobe I/O Runtime. This lesson connects that app to a real Commerce store over REST. By the end, a React table in web-src shows order rows pulled from /rest/V1/orders.

That is the pattern most Commerce App Builder projects start with: Runtime action signs the OAuth request, Commerce returns JSON, the SPA renders it. I/O Events come in lesson 3. This one is plain HTTP.

Prerequisite: complete Lesson 1 (opens in new tab) (Console project, CLI, deployed app). You also need Admin access to a Commerce instance (local, cloud, or staging).

Lesson checklist

  • Create and activate a Commerce integration with scoped REST permissions
  • Copy OAuth credentials into .env without committing them
  • Map Commerce env vars through app.config.yaml into action inputs
  • Add a commerce action that calls Commerce REST with OAuth 1.0a signing
  • Call the action from web-src and render results
  • Test locally with aio app dev, deploy with aio app deploy, verify the hosted URL
  • Use pagination on list endpoints instead of loading the entire catalog

What we are building

Two paths work:

  1. Extend the Hello World app from lesson 1 (best if you want to understand every file you add).
  2. Clone Adobe's sample-extension (opens in new tab) (Experience League still points here) or browse adobe-commerce-samples (opens in new tab) for newer examples, then bind with aio app use.

This lesson walks path 1. When you get stuck on OAuth signing, copy actions/oauth1a.js and actions/utils.js from the sample repo instead of writing them from scratch.


Step 1: Create a Commerce integration

App Builder actions authenticate to Commerce as an integration, not as an admin user session. Commerce uses OAuth 1.0a (consumer key/secret plus access token pair). Adobe documents the full handshake here (opens in new tab). That guide targets PaaS and on-prem Adobe Commerce. Commerce as a Cloud Service may use different REST auth patterns; check your product docs if integrations are not available in Admin.

For App Builder on a classic Commerce Admin, you usually skip the callback dance and copy tokens from Admin after activation.

In Commerce Admin:

  1. Go to System → Extensions → Integrations.
  2. Click Add New Integration.
  3. On Integration Info, set a name (for example App Builder Lesson 2) and email. For Callback URL and Identity Link URL, use placeholder HTTPS URLs if Admin requires them (https://example.com/callback is fine for a manual token copy workflow).
  4. Open the API tab. Set Resource Access to Custom and grant read access to what you need. For orders, enable Sales → Operations → Orders (read). Start narrow. You can widen permissions later.
  5. Click Save, then Activate on the grid row.
  6. Click Allow in the confirmation popup.
  7. Copy all four values from Integration Tokens for Extensions:
    • Consumer Key
    • Consumer Secret
    • Access Token
    • Access Token Secret
  8. Note your store base URL with trailing slash, for example https://mystore.test/.

Store those values in a password manager. They are equivalent to API passwords.


Step 2: Add credentials to .env

From your App Builder project root (same app as lesson 1), append Commerce keys to .env. Never commit this file.

Run
## Adobe Commerce OAuth (Integration)
COMMERCE_BASE_URL=https://mystore.test/
COMMERCE_CONSUMER_KEY=your_consumer_key
COMMERCE_CONSUMER_SECRET=your_consumer_secret
COMMERCE_ACCESS_TOKEN=your_access_token
COMMERCE_ACCESS_TOKEN_SECRET=your_access_token_secret

Stop any running aio app dev process before editing .env. The dev server backs up and rewrites that file while it runs.

Optional for local debugging:

Run
LOG_LEVEL=debug

Step 3: Wire credentials through app.config.yaml

Runtime actions do not read .env directly in production. The CLI maps environment variables into action inputs at deploy time. Add (or extend) an inputs block under your commerce action definition.

yaml
application:
  actions: actions
  web: web-src
  runtimeManifest:
    packages:
      hello-world:
        license: Apache-2.0
        actions:
          commerce:
            function: actions/commerce/index.js
            web: 'yes'
            runtime: nodejs:18
            inputs:
              LOG_LEVEL: $LOG_LEVEL
              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_SECRET
            annotations:
              require-adobe-auth: true
              final: true

Inside the action, read params.COMMERCE_BASE_URL and the other keys. That is how Adobe's samples pass secrets safely.

require-adobe-auth: true means callers must send a valid Adobe IMS bearer token and org header. The generated React UI handles that when you use the App Builder dev template. Turn it off only for isolated experiments, not production.


Step 4: Add the actions/commerce folder

Create this layout:

text
actions/
├── oauth1a.js          # OAuth 1.0a signing helper (copy from app-builder-samples)
├── utils.js            # errorResponse, input validation (copy from samples)
└── commerce/
    └── index.js        # your REST call

The signing helper builds the Authorization header Commerce expects (HMAC-SHA256 over method, URL, and OAuth parameters). Do not reimplement that unless you enjoy reading RFC 5849 on a Friday night. Adobe ships working helpers in app-builder-samples (opens in new tab).

Your action entry point follows this shape:

javascript
const { Core } = require('@adobe/aio-sdk')
const { errorResponse, checkMissingRequestInputs } = require('../utils')
const { getCommerceOauthClient } = require('../oauth1a')
 
async function main(params) {
  const logger = Core.Logger('commerce', { level: params.LOG_LEVEL || 'info' })
 
  try {
    const required = [
      'COMMERCE_BASE_URL',
      'COMMERCE_CONSUMER_KEY',
      'COMMERCE_CONSUMER_SECRET',
      'COMMERCE_ACCESS_TOKEN',
      'COMMERCE_ACCESS_TOKEN_SECRET'
    ]
    const missing = checkMissingRequestInputs(params, required, [])
    if (missing) {
      return errorResponse(400, missing, logger)
    }
 
    const oauth = getCommerceOauthClient(
      {
        url: params.COMMERCE_BASE_URL,
        consumerKey: params.COMMERCE_CONSUMER_KEY,
        consumerSecret: params.COMMERCE_CONSUMER_SECRET,
        accessToken: params.COMMERCE_ACCESS_TOKEN,
        accessTokenSecret: params.COMMERCE_ACCESS_TOKEN_SECRET
      },
      logger
    )
 
    // Paginate. Do not request the entire order history in one call.
    const operation =
      'V1/orders?searchCriteria[pageSize]=10&searchCriteria[currentPage]=1'
 
    const body = await oauth.get(operation)
 
    return {
      statusCode: 200,
      body
    }
  } catch (error) {
    logger.error(error)
    return errorResponse(500, error.message || error, logger)
  }
}
 
exports.main = main

Install any missing dependencies the sample helpers need (often node-fetch or signing libs already in the sample package.json). Run npm install after copying files.


Step 5: Call the action from web-src

The sample pattern uses a React hook that invokes your Runtime action URL and maps the JSON into component state. Adobe's web-src tutorial (opens in new tab) shows useCommerceOrders calling the commerce action and an Orders component rendering a React Spectrum TableView.

Minimal flow:

  1. Add web-src/src/utils/api.js (or reuse the generated one) to POST/GET your commerce action URL with IMS headers.
  2. Add web-src/src/hooks/useCommerceOrders.js to fetch on mount and expose { isLoading, orders }.
  3. Add web-src/src/components/Orders.js with columns like increment_id, status, created_at, grand_total.
  4. Register the component in your app shell (usually web-src/src/index.js or the default route component).

When aio app dev runs, the CLI injects action URLs into the frontend config. After deploy, the same relative paths resolve to hosted Runtime URLs.

If the UI loads but the table stays empty, open browser devtools → Network, inspect the action response. A 401 on the action usually means missing IMS headers. A 401 from Commerce inside the action body means bad OAuth credentials or ACL.


Step 6: Test locally

From the project root:

Run
aio where
aio app dev

Confirm you are on the Stage workspace (or your dev workspace), not Production.

Open the local SPA URL the CLI prints (often https://localhost:9080). Accept the self-signed certificate on first run if prompted.

You should see order rows from Commerce. If the store has no orders yet, place a test order or switch the action to a smaller endpoint (for example store config or a single product by SKU) to prove connectivity first.

To invoke the action directly for debugging, use the sample dev UI's action tester or curl the action URL with the IMS headers Adobe documents in the first app guide (opens in new tab).


Step 7: Deploy and verify the hosted app

Run
aio login
aio where
aio app deploy
aio app get-url

Open the deployed web URL. The UI should behave like local dev, but actions and static assets both run on Adobe infrastructure.

Verify three things:

  1. Hosted SPA loads
  2. Order table populates (or shows a deliberate empty state)
  3. Direct hit on the commerce action URL returns JSON when called with valid IMS auth

Developer Console → your project → workspace still lists the same Runtime URLs if you need to share them with a teammate.


Troubleshooting

401 from Commerce inside the action

Integration not activated, wrong token copied, or missing ACL for GET /V1/orders. Reauthorize the integration and update .env.

Action deploys but params are empty

app.config.yaml inputs not mapped with $VAR syntax, or .env missing keys. Redeploy after fixing both files.

CORS or network errors from the browser

Usually a wrong action URL in frontend config. Restart aio app dev or redeploy so URLs refresh.

SSL errors to Commerce from Runtime

Your Commerce URL must be reachable from Adobe's cloud. A *.test hostname on your laptop will not work after deploy unless Commerce is on a public HTTPS endpoint (staging URL, ngrok, cloud sandbox).

Works locally, fails deployed

Classic sign you pointed at a local-only Commerce base URL. Update COMMERCE_BASE_URL to a URL Runtime can reach, then redeploy.


What to do next

You now have the pull side of composable Commerce: App Builder reads store data over REST. Lesson 3 will flip the direction with Adobe I/O Events (Commerce pushes order-placed payloads to your action without the UI polling).

Until then, try one small extension on your own: add a second action for /V1/products with pageSize=5, or filter orders by status in the REST query string.

Further reading: