[modularizing] feat: @shopify/hydrogen-api package#3725
Draft
fredericoo wants to merge 19 commits intofb-scaffold-micropackagefrom
Draft
[modularizing] feat: @shopify/hydrogen-api package#3725fredericoo wants to merge 19 commits intofb-scaffold-micropackagefrom
@shopify/hydrogen-api package#3725fredericoo wants to merge 19 commits intofb-scaffold-micropackagefrom
Conversation
Extract the framework-agnostic Storefront API client from hydrogen-react into its own zero-dependency package. This is the first step toward a micro-package architecture where API concerns are decoupled from React. Exports: createStorefrontClient, StorefrontClientProps, StorefrontClientReturn, SFAPI_VERSION, storefrontApiCustomScalars. Subpath exports for storefront-api-types and storefront.schema.json. SDK variant headers adapted to hydrogen-api/api for analytics accuracy. getPublicTokenHeadersRaw kept module-private (not promoted to public API). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Disposable proof-of-concept that queries the Storefront API using the low-level createStorefrontClient from hydrogen-api — raw fetch with typed headers, no Hydrogen framework helpers or caching. This validates that hydrogen-api works as a standalone dependency in a real Hydrogen storefront context. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The initial scaffold only had the low-level URL/header builder from hydrogen-react. This upgrades the package to export the complete createStorefrontClient with 1:1 API parity to packages/hydrogen/src/storefront.ts: server caching, i18n, GraphQL validation, error handling, @defer streaming, and MCP request forwarding. Key decisions: - Inlined codegen types (codegen-types.ts) to avoid broken DTS output — tsup's DTS bundler externalizes devDependencies, so importing from @shopify/hydrogen-codegen or graphql would leave unresolvable references in the published .d.ts - Single runtime dep: @shopify/graphql-client (for @defer streaming) - Customer Account API types generated independently (not from hydrogen-react) to keep the package framework-agnostic - Stripped browser-only functions from server-timing.ts - Removed dead logCacheApiStatus code and unused HYDROGEN_SFAPI_PROXY_KEY - Unified duplicate warnOnce into shared utils/warning.ts - Added package to lintedTSPackages and fixed all TSDoc violations Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The codegen's default `interfaceExtension` now emits a second `declare module '@shopify/hydrogen-api'` block alongside the existing `@Shopify/hydrogen` one. This enables type-safe `storefront.query()` responses when using `@shopify/hydrogen-api` directly. The `replacePlaceholders` function was changed from `.replace()` to `.replaceAll()` because there are now two occurrences of each placeholder in the template string. Also updated `getSchema()` to fall back to `@shopify/hydrogen-api` when `@Shopify/hydrogen` is not installed, supporting projects that use hydrogen-api standalone. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The schema and type files were static copies from hydrogen-react with no way to regenerate them. This adds an independent codegen pipeline that hits the same live Shopify API endpoints to produce: - src/storefront-api-types.d.ts - storefront.schema.json - src/customer-account-api-types.d.ts - customer-account.schema.json Run `pnpm run graphql-types` to regenerate after a Storefront API version bump. The config mirrors hydrogen-react's codegen.ts so both packages stay in sync by hitting the same endpoints. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous build relied on tsup's onSuccess hook, which is unreliable when `clean: true` is set in array configs — schemas copied but .d.ts files did not. Replaced with a dedicated `copy-assets` npm script that explicitly copies all 4 non-bundled assets (2 API type .d.ts files + 2 schema JSONs) into dist/. Schema export paths now point to dist/ so the `files` array can be a single entry. Also includes regenerated API type files from the new codegen pipeline (whitespace-only diffs from the previous manual copies). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
tsup's multi-phase build (ESM → onSuccess → DTS) with `clean: true` wipes files copied during onSuccess when the DTS phase re-cleans the output folder. tsdown (powered by rolldown) runs JS + DTS as a single pass, then fires post-build hooks — eliminating the race condition. Key changes: - Replace tsup with tsdown (v0.12.9, rolldown v1.0.0-rc.16) - Use tsdown's built-in `copy` option for asset files instead of a separate copy-assets npm script or onSuccess hack - Use `inputOptions.transform.define` for __HYDROGEN_DEV__ replacement (rolldown's Oxc transformer handles this; tsdown's top-level `define` leaks into rolldown input validation with a harmless warning) - Set `hash: false` for predictable output filenames Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
v0.12 had two issues:
- `define` option produced a spurious "Invalid input options" warning
from rolldown and required an `inputOptions.transform.define`
workaround to actually perform replacements
- `copy` entries with `{from, to}` treated `to` as a file path
Both are fixed in v0.21.9:
- Top-level `define` works correctly (no warning, replacements apply)
- `copy` accepts `from` as an array with `to` as a directory target
- `fixedExtension: false` restores `.js`/`.d.ts` output extensions
(v0.21 defaults to `.mjs`/`.d.mts`)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
If the \`define\` option ever breaks (config typo, tsdown regression, accidental removal), dev-only warnings would ship to consumers and their bundlers would hit a ReferenceError on the undeclared global at runtime — since \`__HYDROGEN_DEV__\` has no declaration, it's a free variable that only works because the bundler replaces it at build time. Added a build:done hook that scans all output chunks for the literal string and throws with a clear error pointing at the config option. Verified both paths: normal build passes, commented-out define fails the build with exit code 1. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Moves storefront/customer-account types and schema JSONs into a single src/generated/ directory so source layout mirrors dist/generated/ 1:1. Why: previously the 4 generated artifacts were scattered — .d.ts files in src/ alongside hand-authored code, schema JSONs at package root. The tsdown copy rule had to enumerate them individually. Co-locating them makes the intent self-evident (anything under src/generated/ is regenerated by `pnpm graphql-types`) and lets the copy rule become a simple directory mirror. How: updated codegen.ts output paths, tsdown.config.ts copy (now `from: 'src/generated/*'` flattens into `dist/generated/`), and the two type-only imports in src/storefront.ts. No change to public exports — consumers still import from `@shopify/hydrogen-api/storefront-api-types` etc., all backed by ./dist/generated/... paths in the exports map. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4921967 to
49d8df0
Compare
…API versions Adds a unit test that fails if SF_API_VERSION or CA_API_VERSION in api-versions.ts doesn't match the version the checked-in .d.ts files were generated against — i.e. someone bumped a version constant without running `pnpm graphql-types` to regenerate. Why: the generated .d.ts files are committed to the repo so consumers and CI get deterministic installs. That's only safe if what ships matches what's declared. Without this check, a version bump in isolation silently ships stale types, and `storefront.query()` calls typecheck against the wrong schema shape. How: extracted SF_API_VERSION and CA_API_VERSION from codegen.ts into src/api-versions.ts as a single source of truth imported by both codegen.ts (the writer) and src/generated-files.test.ts (the verifier). The test reads the "Based on <API> API <version>" header codegen.ts embeds on line 3 of each .d.ts and asserts the parsed version equals the declared one. Uses parametrized `it.each` so each API gets its own named test; the failure message names the file, both versions, and the remediation command. Unit test rather than a tsdown hook: this is an invariant on repo state (are checked-in files fresh?), not on build output (did bundling produce correct JS?) — it belongs with the other `pnpm test` assertions where failures are discoverable and compose with other invariants. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
49d8df0 to
38d9284
Compare
Contributor
|
Oxygen deployed a preview of your
Learn more about Hydrogen's GitHub integration. |
Adds packages/hydrogen-api/src/generated/ to .prettierignore so the
minified introspection schema JSONs don't trip prettier's check.
Why: graphql-codegen emits those schema JSONs minified (see
`{introspection: {minify: true}}` in codegen.ts). Running prettier
over a minified schema would re-inflate it, doubling the file size
and producing massive diff noise on every bump — for zero benefit,
since the files are consumed programmatically and never hand-edited.
Ignoring the whole generated directory also future-proofs: any new
output graphql-codegen adds is covered automatically.
Follows the existing precedent in .prettierignore for
packages/hydrogen-react's equivalent schema files.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
93327cc to
5571dd6
Compare
@shopify/hydrogen-api package
Reverts skeleton template to match main — the POC route, workspace dep, the generated type augmentation, and the corresponding pnpm-lock entry were local verification aids, not part of the shippable package. Reviewers can restore them by checking out the previous commit (see PR description for test instructions). Why: keeping skeleton untouched in the merged PR avoids coupling this hydrogen-api scaffold to a skeleton release and keeps the diff focused on the new package itself. Folding the pnpm-lock.yaml revert into this same commit means `git reset --hard HEAD~1` restores the entire POC verification state atomically — package.json, lockfile, and all. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
a541681 to
13012bd
Compare
@shopify/hydrogen-api package@shopify/hydrogen-api package
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WHY are these changes introduced?
We want a framework-agnostic entrypoint for the Storefront API — no React, no Remix, no
hydrogenpeer. This PR lands@shopify/hydrogen-apiwithcreateStorefrontClient, the cache strategies, GraphQL helpers, and the generated Storefront + Customer Account API types/schemas. It's the first of the split packages that should let us ship the Storefront client to consumers who don't want (or can't use) the full Hydrogen framework.Why tsdown instead of tsup
Early in this work we hit a sharp edge in
tsup: withclean: trueand an array config, the DTS pass runs afteronSuccess, re-cleans the output dir, and erases files theonSuccesshook just copied. That meant our generated.d.tsfiles (Storefront API types, Customer Account API types) and the schema JSONs were silently disappearing fromdist/on every build. TSDown does not have this issue, builds faster and has acopyutil that makes it even simplerWHAT is this pull request doing?
packages/hydrogen-api/with the full server-side Storefront client (1:1 API parity withpackages/hydrogen/src/storefront.ts), cache layer, GraphQL utilities, and request helpersgraphql-codegen, co-located undersrc/generated/for source↔dist symmetrytsdownbuild with a safety net: fails the build if the dev flag wasn't replacedpackages/hydrogen,packages/hydrogen-react,packages/hydrogen-core, andtemplates/skeletonuntouched in the merged diffHOW to test your changes?
The final commit on this branch removes the skeleton wiring we used to verify the package end-to-end. To reproduce the verification locally:
declare module '@shopify/hydrogen-api'augmentation we add in the POC state):dist/index.d.tsexports resolve correctly and the Storefront/Customer Account type augmentation threads through:@shopify/hydrogen-apidirectly:pnpm --filter skeleton dev # then open http://localhost:3000/api-poccreateStorefrontClient→storefront.query()→ cache → response, all through the new package.Post-merge steps
None.
Checklist