adobe/skills

commerce-app-api-mesh

Scaffold or update an Adobe API Mesh configuration (mesh.json) in front of a Commerce app: add GraphQL/OpenAPI sources, extend an existing Commerce GraphQL type with a new field, and wire a cross-source resolver for it.

View source

How to use

  1. Copy the installation command and review the source package before installing it in your own agent environment.
  2. Check the source document for required tools, credentials and supported hosts.
  3. Describe your input and desired deliverable; review the output against the source material.
npx skills add https://github.com/adobe/skills --skill commerce-app-api-mesh

Before you start

  • Installation does not include a model subscription, API allowance or permission to access your files.
  • Repository stars and installation counts describe activity, not an independent quality or security test.
Original skill document

Rendered from the source repository. Headings, examples, code, tables, links, and referenced images are preserved.

Wire API Mesh in Front of a Commerce App

Composes Commerce's own GraphQL API and this app's runtime actions into a single mesh schema. Two moves this skill covers: exposing a runtime action as a mesh source, and extending an existing Commerce type with a field resolved by delegating to that source.

This skill assumes general API Mesh knowledge (mesh.json anatomy, handler types, transforms, hooks, secrets, CORS, generic declarative/programmatic resolvers). If any of that is unfamiliar, load it from Adobe's own material first — see References — rather than guessing at syntax. None of that material covers extending an existing Commerce type via additionalResolvers (targetTypeName/sourceTypeName/requiredSelectionSet/sourceSelectionSet) or wrapping an aio-commerce-sdk runtime action as a mesh source — that's what follows.

Prerequisites

  • aio plugins install @adobe/aio-cli-plugin-api-mesh is installed.
  • If exposing a runtime action as a source, it's already built and deployed with a real, reachable HTTPS endpoint — a source pointing at an undeployed action fails opaquely.
  • Check whether a mesh already exists for this workspace: aio api-mesh:get. "No mesh found" → you'll create; otherwise you're editing an existing mesh.json and will update.

Step 1 — Confirm schema shapes via introspection

Before writing additionalTypeDefs or additionalResolvers, introspect the Commerce (or other) GraphQL source you're extending. Don't assume a type/field name from memory or a similar-sounding convention — near-miss names produce a mesh that builds successfully but whose resolver never fires.

bash
curl -s -X POST "<graphql-endpoint>" -H "Content-Type: application/json" \
  -d '{"query":"{ __type(name: \"<TargetType>\") { fields { name } } }"}'

Step 2 — Scaffold sources

json
{
  "name": "Commerce",
  "handler": {
    "graphql": {
      "endpoint": "<commerce-graphql-endpoint>",
      "operationHeaders": { "Authorization": "{context.headers.authorization}" }
    }
  }
}

Include operationHeaders by default on any source whose schema has customer-, cart-, or session-scoped fields — API Mesh does not forward the caller's Authorization header automatically. Omitting it makes every authenticated query fail with the backend's own generic "not authorized" error, indistinguishable from an invalid token.

To wrap a runtime action, write a small static OpenAPI document describing just its endpoint and reference it by relative path:

json
{
  "name": "<SourceName>",
  "handler": { "openapi": { "source": "./mesh/<source>.json" } }
}

The OpenAPI document itself needs enough shape for the mesh to generate a Query field from it — not just the pointer above. Minimal example for a single-endpoint runtime action:

json
{
  "openapi": "3.0.0",
  "info": { "title": "<SourceName>", "version": "1.0.0" },
  "servers": [{ "url": "<runtime-action-base-url>" }],
  "paths": {
    "/<action-path>": {
      "get": {
        "operationId": "<sourceField>",
        "parameters": [
          {
            "name": "<arg>",
            "in": "query",
            "required": true,
            "schema": { "type": "string" }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "<resultField>": { "type": "string" } }
                }
              }
            }
          }
        }
      }
    }
  }
}

operationId becomes the Query field name — it must match sourceFieldName in Step 3's resolver exactly, or the resolver builds successfully but never fires.

The declared response schema must match what the action actually returns — the mesh parses according to what you declare, it doesn't reshape data.

Step 3 — Extend a type and wire the resolver

json
"additionalTypeDefs": "extend type <TargetType> { <newField>: String }",
"additionalResolvers": [
  {
    "targetTypeName": "<TargetType>",
    "targetFieldName": "<newField>",
    "sourceName": "<SourceName>",
    "sourceTypeName": "Query",
    "sourceFieldName": "<sourceField>",
    "requiredSelectionSet": "{ <keyField> }",
    "sourceArgs": { "<arg>": "{root.<keyField>}" },
    "sourceSelectionSet": "{ <resultField> }",
    "result": "<resultField>"
  }
]

Always pair sourceSelectionSet with result when extracting a scalar from an object-returning source field — never use result alone. The result-only path builds its selection set by hand instead of via the GraphQL parser, and breaks with "No type was found for field node ... __typename" specifically when the target field resolves inside a list (e.g. a parent's items[].<newField>). A direct root-query call to the same source field succeeds even when this bug is present, so that test alone isn't sufficient proof the resolver works.

Step 4 — Deploy and verify

If you already know a browser-based app will call this mesh, decide responseConfig.CORS now, before your first deploy — the browser-verification tier below exists to catch a missed CORS config, but deciding upfront avoids a second deploy cycle.

The first aio api-mesh:* call in a session opens an interactive browser login (Waiting for browser login...). An agent without browser access can't complete this itself — hand the printed login URI to the human and wait.

sh
aio api-mesh:create mesh.json -c   # first time
aio api-mesh:update mesh.json -c   # subsequent edits

-c/--autoConfirmAction skips the interactive Are you sure you want to update the mesh: <id>? prompt. That prompt is the only checkpoint before mutating a mesh other people or systems may already depend on — reserve -c for a workspace-scoped mesh you just created yourself (e.g. in CI, or a throwaway dev workspace). When updating an existing, shared, or already-deployed mesh, omit -c and have a human confirm the prompt, or at minimum get explicit human sign-off on the diff before running the command — treat this like any other live-infrastructure change, not a routine CLI call.

Provisioning is asynchronous — poll rather than assume completion:

sh
until aio api-mesh:status 2>&1 | grep -qi success; do sleep 20; done

Verify in two tiers: first the source's root field directly, then the field in its real nested/authenticated shape (a list-nested query with a real caller credential, not a flat root-field call). Tier 1 passing does not prove tier 2 works — the bug above is invisible in tier 1.

If the consuming app will call this mesh directly from a browser (not just server-to-server), add a third tier: a real request from that app's actual origin. The two tiers above only prove server-side reachability — a mesh with no responseConfig.CORS entry for that origin passes both while still failing every browser call through it, not just the new field (see the CORS section of the api-mesh-starter-kit reference below).

Common Issues

  • `"not authorized"` on an authenticated query, even with a valid token — the source's graphql handler is missing operationHeaders. Check mesh.json, not the token.
  • `"No type was found for field node ... __typename"` on a nested/list field, but the source works fine at root — the resolver uses result without sourceSelectionSet. Add it.

Quality Bar

  • aio api-mesh:status reports success, and the new field resolves correctly in its real nested/authenticated shape, not just at the source's root field.
  • If a browser-based app will call this mesh, that app can complete a real request against it — not just aio api-mesh:status and curl.

Chaining

  • The source doesn't exist yet as a runtime action — invoke commerce-app-storage, commerce-app-webhooks, or commerce-app-eventing to scaffold and deploy it first.

References

  • API Mesh prompting guide — Adobe's own guidance for prompting an agent to write mesh configs; general workflow and expectations
  • api-mesh-starter-kit llm.txt — reference knowledge base covering mesh.json anatomy, all three handler types, transforms, hooks, secrets, context state, CORS, and the CLI command set

Related by task

from this repository

More skills

All skills
adobe
Community

adobe-edit-quick-cut

Create a punchy highlight cut from a video with Adobe Quick Cut — for requests like "make a sizzle reel", "make a highlight reel", "quick cut this", "cut the best parts", "shorten this video", "make a highlight clip", or "summarize this video visually". Works from the user's original request without follow-up questions and produces one highlight cut. Do NOT use it for exact-timestamp trims ("cut from 0:30 to 1:15") or other deterministic edits — Quick Cut selects moments by relevance, not exact

installs
1
GitHub stars
306
Updated
Oct 24
adobe
Community

appbuilder-project-init

Initialize an Adobe App Builder project end-to-end and prepare the machine to build one. Creates the Console project and workspace, subscribes APIs (including those needing a product profile), maps intent to the right template, runs non-interactive aio app init, and guides post-init customization. Use whenever the user mentions creating an App Builder app, scaffolding a project, aio app init, an Experience Cloud extension, adding actions or web assets, or creating a Console project/workspace — e

installs
2
GitHub stars
268
Updated
Oct 20
adobe
Community

commerce-app-admin-ui

Add or modify Adobe Commerce Admin UI extensions on the commerce/backend-ui/2 extension point: custom grid columns, mass actions, order view buttons, a custom Admin menu entry, and custom ACL resources. Use whenever the user wants to extend the Commerce Admin — add a column to the order, product, or customer grid, add a bulk/mass action to a grid, add a button to the order view page, add a custom menu item or page, or declare custom permissions the app checks itself — even when they don't name t

installs
2
GitHub stars
268
Updated
Oct 20
adobe
Community

commerce-app-business-config

Manage custom business configuration in an Adobe Commerce app. Use when the user wants to add, modify, or remove merchant-configurable settings (config fields, admin config, store configuration) exposed through Commerce Admin. Creates typed config fields (text, password, email, url, tel, boolean, list) in businessConfig.schema. Requires a base app initialized with commerce-app-init.

installs
2
GitHub stars
268
Updated
Oct 20