Skip to content

[Feature] Add server-side App Events logging - #3368

Open
kristianpd wants to merge 3 commits into
Shopify:mainfrom
kristianpd:app-events-log
Open

[Feature] Add server-side App Events logging#3368
kristianpd wants to merge 3 commits into
Shopify:mainfrom
kristianpd:app-events-log

Conversation

@kristianpd

@kristianpd kristianpd commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

WHY are these changes introduced?

Apps need to send server-side App Events without a shop session. The Global API uses an app-level client-credentials token, while the existing clientCredentials({shop}) flow returns a shop-scoped Admin API session.

WHAT is this pull request doing?

  • Adds shopify.auth.globalApiClientCredentials() to mint and cache app-level Global API tokens from the configured API key and secret.
  • Adds shopify.log() to validate and send one App Event, refresh once after a 401, and report idempotent replays.
  • Adds a configurable HTTPS Global API base URL for non-production Shopify environments.
  • Coalesces cold, expiry, and rejected-token refresh requests per SDK instance.
  • Reuses concurrent replacement tokens and conditionally clears exact rejected tokens after refresh failures or retry 401s.
  • Redacts credential request bodies from HTTP debug logs.
  • Documents both public APIs and adds a minor changeset.

Type of change

  • Patch: Bug (non-breaking change which fixes an issue)
  • Minor: New feature (non-breaking change which adds functionality)
  • Major: Breaking change (fix or feature that would cause existing functionality to not work as expected)

Verification

  • Focused App Events/token cache tests — 35 tests passed
  • pnpm --filter @shopify/shopify-api test — 693 tests passed
  • pnpm --filter @shopify/shopify-api build
  • pnpm lint
  • pnpm prettier
  • Local Global API smoke: token minting, event ingestion, and replay passed for 2026-07, 2026-10, and unstable

Checklist

  • I have used pnpm changeset to create a draft changelog entry (do NOT update the CHANGELOG.md files manually)
  • I have added/updated tests for this change
  • I have documented new APIs/updated the documentation for modified APIs (for public APIs)

@github-actions github-actions Bot added the devtools-gardener Post the issue or PR to Slack for the gardener label Aug 24, 2026
@kristianpd kristianpd closed this Aug 25, 2026
@kristianpd kristianpd reopened this Aug 25, 2026
@kristianpd
kristianpd force-pushed the app-events-log branch 2 times, most recently from 82744d3 to fd875eb Compare August 27, 2026 12:28
@kristianpd
kristianpd marked this pull request as ready for review August 27, 2026 12:28
@kristianpd
kristianpd force-pushed the app-events-log branch 5 times, most recently from 91f9f28 to cf13a70 Compare August 27, 2026 14:52
Comment thread packages/apps/shopify-api/lib/config.ts Outdated
hostName: '',
hostScheme: 'https',
globalApiUrl: 'https://api.shopify.com',
globalApiVersion: GlobalApiVersion.July26,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

welcome advice on how to default here to be backward compatible

Add `shopify.appEvents.log()` for sending one App Event to Shopify, and
`shopify.auth.globalApiClientCredentials()` for minting the app-level token
it uses. Both authenticate with the `apiKey` and `apiSecretKey` already in
the config, so no shop session is required.

The token is cached per config until shortly before it expires, concurrent
mints are coalesced into one request, and a 401 refreshes the token and
retries the event exactly once.

Validate every documented constraint before making a request: shop ID form,
idempotency key length, attribute count, key pattern, value types, string
length, and the five-minute future bound on the timestamp. `attributes` is
required by the API, so it is required here and always sent, empty or not.

Pin the Global API version the library targets rather than deriving it from
`config.apiVersion`. The Global API is versioned separately from the Admin
API even where the version strings line up. Add `globalApiUrl` to reach a
non-production Global API.

Keep the token request body out of the debug logs through a new `logBody`
option, and serialize the top-level `error` and OAuth `error_description`
that the Global API returns so a rejection no longer throws with nothing
from the server in the message.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

devtools-gardener Post the issue or PR to Slack for the gardener

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants