Skip to content

Add Shopify on Cloudflare template - #1075

Open
Uzair-Ahmed-Dahraj wants to merge 14 commits into
cloudflare:mainfrom
devkindhq:add-shopify-on-cloudflare-template
Open

Uzair-Ahmed-Dahraj wants to merge 14 commits into
cloudflare:mainfrom
devkindhq:add-shopify-on-cloudflare-template

Conversation

@Uzair-Ahmed-Dahraj

@Uzair-Ahmed-Dahraj Uzair-Ahmed-Dahraj commented Jul 24, 2026 •

Copy link
Copy Markdown

Description

Adds a new template: shopify-on-cloudflare-template — a production-ready Shopify embedded-app starter on Cloudflare Workers (Hono, D1 + Drizzle ORM, KV-backed Shopify session storage, R2, React 18 + Polaris), with session-token (JWT) auth via App Bridge v4, a public /preview page, unit tests, and a Playwright smoke test.

Built and maintained by Devkind. Source repository: https://github.com/devkindhq/shopify-on-cloudflare

▶️ Live preview (opens without a Shopify login): https://cloudflare-shopify-starter-template.ali-d43.workers.dev/preview

The app's authenticated home page only renders inside the Shopify Admin iframe, so the link above goes to the public /preview route, which shows what the template is and how it is built.

preview-16x9-hero

Checklist

  • Template Metadata
    • template directory ends with -template
    • "cloudflare" section of package.json is populated
    • template preview image uploaded to Images
    • README is populated and uses <!-- dash-content-start --> and <!-- dash-content-end --> to designate the Dash readme preview
    • .gitignore file exists
    • package.json contains a deploy command
    • package.json contains private: true and no version field

Notes for reviewers

  • The authenticated Polaris UI only renders inside the Shopify Admin iframe (needs a host param + API key via App Bridge), so the Playwright smoke tests cover the parts that hold without a Shopify session: the public /preview page, the health endpoint, and the requireShop middleware rejecting unauthenticated /api/* requests.
  • wrangler.jsonc ships placeholder binding IDs (YOUR_DATABASE_ID, YOUR_KV_NAMESPACE_ID); the README documents creating the D1/KV/R2 resources.
  • Verified locally against the repo checks: pnpm -w check gates (templates lint, prettier, npm-lockfiles), pnpm install --frozen-lockfile, cf-typegen idempotency, template unit tests (20 passing), and vite build && wrangler deploy --dry-run.

A production-ready Shopify embedded-app starter on Cloudflare Workers:
Hono routing, session-token (JWT) auth via App Bridge v4, Drizzle ORM on
D1, KV-backed Shopify session storage, R2 file storage, and a React 18 +
Polaris frontend. Includes a public /preview page, unit tests, and a
Playwright smoke test.
Uzair-Ahmed-Dahraj and others added 5 commits August 3, 2026 09:04
Adds a 16:9 screenshot (3200x1800) of the template's public /preview page
and references it in the README above the dash-content marker, matching
workers-for-platforms-template.

The committed asset is also what a Cloudflare team member can upload to
set cloudflare.preview_image_url during review.
… shopify-on-cloudflare-template

Ports devkindhq/shopify-on-cloudflare#9 into the template:

1. Webhook HMAC verification compared the computed digest against the
   X-Shopify-Hmac-Sha256 header with plain !== (early-exit string
   compare), leaving it open to timing attacks. Replaced with
   crypto.subtle.timingSafeEqual (Workers runtime extension, per
   developers.cloudflare.com/workers/runtime-apis/web-crypto) with a
   constant-time XOR-fold fallback for non-Workers runtimes (vitest/Node).
   Adds a wrong-length-signature test covering the no-early-exit path.

2. Bumps @cloudflare/vite-plugin 1.46.0 -> 1.52.1 and wrangler
   4.113.0 -> 4.123.0 (peer dep of the new vite-plugin). Clears all 8
   high-severity npm audit advisories (undici chain). Remaining 6
   moderates are drizzle-kit/react-router major-bump chains, documented
   as out of scope upstream.

3. Adds the security-audit note above registerWebhooks (verified safe:
   outbound registration, not inbound handling).

Verified: templates lint, prettier, lockfile lint, turbo check +
cf-typegen (build + deploy dry-run), vitest 21/21, wrangler types
idempotent vs committed worker-configuration.d.ts.
fix(security): timing-safe webhook HMAC + clear high-severity deps (port of shopify-on-cloudflare#9)
…dflare-template

# Conflicts:
#	pnpm-lock.yaml
#	templates.json
…with new pin-all-deps CI gate

- Resolve pnpm-lock.yaml and templates.json conflicts against upstream main
- Comply with new syncpack 'pin all deps' rule (upstream cloudflare#1114): exact-pin all shopify template deps
- versionGroups for peer-dep-constrained stack (same mechanism as remix's React 18):
  * React ^18 (@shopify/app-bridge-react@4 + @shopify/polaris@10 peer-cap at 18)
  * eslint 8.57.1 + eslint-plugin-react-hooks 4.6.0 (eslint-config-airbnb is eslint-8-only)
  * hono 4.13.2 (high advisories <=4.12.33: JWT auth-bypass GHSA-r38q-p5jb-mjvw, XSS GHSA-f9qg-mpr5-mhfp)
  * react-router-dom 7.18.2 (high advisory <=7.17.0: turbo-stream RCE)
- Cross-template syncpack alignment (HighestSemverMismatch): bump @typescript-eslint/plugin-react-swc in remix, to-do-list, workflows, x402
- Fix E2E backlink locator: exact match (GitHub link text contains 'devkindhq', broke strict mode)

Gates: check:deps/templates/lockfiles/prettier OK; turbo check+cf-typegen 3/3; vitest 21/21; playwright 5/5; npm audit 0 high/0 critical
The pin-all-deps gate (upstream cloudflare#1114) only needed our two mismatched dev
tools brought in line with what upstream already uses. Bumping the four
other templates up was not required.

Reverts remix-starter, to-do-list-kv, workflows-starter and x402-proxy to
upstream, and sets ours to @typescript-eslint 8.50.0 and
@vitejs/plugin-react-swc 4.0.0. Regenerates templates.json,
package-lock.json and pnpm-lock.yaml.

Security posture is unchanged: wrangler 4.123.0, @cloudflare/vite-plugin
1.52.1, vitest 4.1.10, hono 4.13.2 and react-router-dom 7.18.2 are all
kept, and none of the moved packages is in an advisory chain. npm audit
reports 0 high, 4 moderate (drizzle-kit -> esbuild only).
Resolve upstream conflict without touching other templates
Upstream cloudflare#1137 pinned all 37 templates to wrangler 4.127.0, because
Wrangler 4.126.0 and later emit CI annotations that link Preview
deployments to their source repo, commit and PR. It regenerated the
derived files and dropped the Remix wrangler-3 version group.

pnpm-lock.yaml and templates.json conflicted. Both are generated
registries with one entry per template, so both intents fit: took
upstream's copy of each, then regenerated so our template's entry is
re-added.

Our template joins the bump:
- wrangler 4.123.0 -> 4.127.0
- package-lock.json and pnpm-lock.yaml regenerated
- templates.json hash now 39611d13
- worker-configuration.d.ts regenerated under 4.127.0, matching what
  upstream did for every other template

Verified: frozen-lockfile up to date, check:deps, check:templates,
check:lockfiles, check:prettier, version validator, 21/21 tests, vite
build, wrangler deploy --dry-run, wrangler types idempotent. Diff vs
upstream/main touches no other template.
@Uzair-Ahmed-Dahraj

Copy link
Copy Markdown
Author

@deloreyj When you have a moment, could you approve the workflows on this PR and add the allow preview label?

CI has not run on this PR yet. All workflow runs have sat at action_required since it was opened, and the runs from 3 August expired after 30 days with zero jobs executed. That produced three "Run failed" notifications which were the expiry, not test failures.

I have just merged main to pick up Wrangler 4.127.0 from #1137, so the branch is conflict-free and current. Our template joins that bump (4.123.0 to 4.127.0) with package-lock.json, pnpm-lock.yaml, the templates.json hash and worker-configuration.d.ts all regenerated.

Verified locally before pushing:

  • pnpm install --frozen-lockfile: lockfile up to date
  • check:deps, check:templates, check:lockfiles, check:prettier: pass
  • validate-version-private-package-json: pass
  • 21/21 unit tests, vite build, wrangler deploy --dry-run: pass
  • wrangler types idempotent (identical output on a second run, so git diff --exit-code is clean)

The diff against main is our template directory plus four shared files: pnpm-lock.yaml, templates.json, our own playwright-tests spec, and one .syncpackrc.json version group. That group keeps this template on React 18, since Polaris 10.45 and App Bridge cap React at 18. It mirrors the existing "Keep React 18 for legacy Remix templates" group. No other template is modified.

Live preview (no Shopify login needed): https://cloudflare-shopify-starter-template.ali-d43.workers.dev/preview

Happy to rebase or change anything you would prefer done differently.

The template carried an ESLint config from a repo with no Prettier,
where `eslint --fix` was the de facto formatter (spaces, single quotes).
This monorepo already formats with Prettier via .editorconfig (tabs,
double quotes), enforced by check:prettier. Two formatters, opposite
settings, same files.

That produced 426 errors (no-tabs 206, indent 101, quotes 65,
react/jsx-indent 44, and 10 more), and worse: `lint` shipped with
--fix, so running it rewrote 6 files and made check:prettier fail. A
contributor running the template's own lint script broke CI.

Fixed both ways ESLint was formatting:
- extends eslint-config-prettier, which disables the conflicting rules,
  and dropped the explicit "quotes" override. Pinned 10.1.8 to match
  x402-proxy-template, so cross-template alignment is unchanged.
- removed --fix from the lint script, so it can only report. None of
  the 7 upstream templates with a lint script use --fix.

Verified: eslint 0 errors (was 426), `npm run lint` changes 0 files
(was 6), check:prettier passes after running lint, plus check:deps,
check:templates, check:lockfiles, frozen-lockfile, 21/21 unit tests,
vite build, wrangler deploy --dry-run, and 5/5 Playwright e2e.
Eight upstream commits, including the Cloudflare Docs voice agent
template and a run of live-demo peer dependency fixes. pnpm-lock.yaml
and templates.json conflicted, as they do on every upstream merge:
both are generated registries with one entry per template.

Resolved by taking upstream's copy of each, then regenerating so our
entry is re-added. No dependency changes were needed this time, since
our pins already match upstream (wrangler 4.127.0, vite-plugin 1.52.1,
vitest 4.1.10, typescript 5.9.3).

templates.json now carries a trailing newline upstream, though the CLI
writer still emits none. Matched the committed file so our diff is only
the added entry.

Verified: frozen-lockfile up to date, check:deps, check:templates,
check:lockfiles, check:prettier, templates.json not rewritten by the
tooling, wrangler types idempotent, eslint 0 errors, 21/21 unit tests,
vite build, wrangler deploy --dry-run, 5/5 Playwright e2e. Diff vs
upstream/main touches no other template.
One upstream commit (cloudflare#1151) that publishes the Cloudflare Docs voice
agent template and updates its package_json_hash. templates.json was
the only conflict: upstream edited the voice-agent hash line, and our
entry sits immediately after it, so git saw overlapping edits in the
same region.

Kept upstream's new hash (49cdbd36) and re-appended our entry. No
dependency changes needed; our pins already match upstream (wrangler
4.127.0, vite-plugin 1.52.1, vitest 4.1.10, typescript 5.9.3). pnpm-lock
.yaml was untouched upstream, so it did not conflict.

Verified: frozen-lockfile up to date, check:deps, check:templates,
check:lockfiles, check:prettier, templates.json not rewritten by the
tooling, wrangler types idempotent, eslint 0 errors, 21/21 unit tests,
vite build, wrangler deploy --dry-run, 5/5 Playwright e2e. Diff vs
upstream/main touches no other template.
Upstream cloudflare#1155 moved all 41 templates to Wrangler 4.136.1, so check:deps
fails until ours matches. The bump also rewrites worker-configuration.d.ts,
which check:turbo compares with git diff --exit-code.
Kept both new templates.json entries: upstream's grpc-container-template
and ours. The file is a registry keyed by name, so neither replaces the other.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants