Skip to content

Commit 7924bdd

Browse files
committed
fix(shop): hard-code Shopify store identity to unblock Netlify scanner
Netlify's secrets scanner treats every env var value as sensitive and fails the build when any of those values appear in the output, with no semantic awareness of which ones are public. SHOPIFY_STORE_DOMAIN (tanstack-2.myshopify.com) and SHOPIFY_API_VERSION (2026-01) are public by design — Shopify prints the domain on every hosted-checkout URL, order email, and receipt, and the version is a platform-wide identifier. Moving both to source constants in src/server/shopify/fetch.ts: - Keeps them out of the env-var watchlist entirely (no scan false positive, no per-site SECRETS_SCAN_OMIT_KEYS config needed) - Makes the store identity explicit in the codebase where the fetch helper lives - Leaves actual secrets (SHOPIFY_PRIVATE_STOREFRONT_TOKEN) in env After this change, the only Shopify env var the deploy needs is SHOPIFY_PRIVATE_STOREFRONT_TOKEN.
1 parent 7bda760 commit 7924bdd

2 files changed

Lines changed: 18 additions & 13 deletions

File tree

src/server/shopify/fetch.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
import { env } from '~/utils/env'
22

3+
/**
4+
* Shopify store identity. Hard-coded intentionally — these are public-by-
5+
* design values (they appear in every Shopify-hosted checkout URL, order
6+
* email, and receipt) and baking them into source keeps them out of the
7+
* environment-variable scanner's watchlist without losing any security.
8+
*
9+
* Only the tokens are real secrets and remain in env vars.
10+
*/
11+
const SHOPIFY_STORE_DOMAIN = 'tanstack-2.myshopify.com'
12+
const SHOPIFY_API_VERSION = '2026-01'
13+
314
type ShopifyFetchInput<TVariables> = {
415
query: string
516
variables?: TVariables
@@ -35,13 +46,11 @@ export async function shopifyServerFetch<
3546
TData,
3647
TVariables = Record<string, unknown>,
3748
>(input: ShopifyFetchInput<TVariables>): Promise<TData> {
38-
const domain = env.SHOPIFY_STORE_DOMAIN
3949
const token = env.SHOPIFY_PRIVATE_STOREFRONT_TOKEN
40-
const version = env.SHOPIFY_API_VERSION
4150

42-
if (!domain || !token) {
51+
if (!token) {
4352
throw new ShopifyError(
44-
'Shopify server client is not configured. Set SHOPIFY_STORE_DOMAIN and SHOPIFY_PRIVATE_STOREFRONT_TOKEN in .env.local.',
53+
'Shopify server client is not configured. Set SHOPIFY_PRIVATE_STOREFRONT_TOKEN in the environment.',
4554
)
4655
}
4756

@@ -53,7 +62,7 @@ export async function shopifyServerFetch<
5362
if (input.buyerIp) headers['Shopify-Storefront-Buyer-IP'] = input.buyerIp
5463

5564
const response = await fetch(
56-
`https://${domain}/api/${version}/graphql.json`,
65+
`https://${SHOPIFY_STORE_DOMAIN}/api/${SHOPIFY_API_VERSION}/graphql.json`,
5766
{
5867
method: 'POST',
5968
headers,

src/utils/env.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,10 @@ const serverEnvSchema = v.object({
1515
RESEND_API_KEY: v.optional(v.string()),
1616
SENTRY_DSN: v.optional(v.string()),
1717
TANSTACK_MCP_ENABLED_TOOLS: v.optional(v.string()),
18-
// Shopify Storefront API — server-only. Cart reads and mutations run
19-
// through createServerFn (src/utils/shop.functions.ts), so the public
20-
// token is never exposed to the browser. The private token is preferred
21-
// for its higher rate limits; the public token is kept as an optional
22-
// fallback for environments where a private token isn't provisioned.
23-
SHOPIFY_STORE_DOMAIN: v.optional(v.string()),
24-
SHOPIFY_API_VERSION: v.optional(v.string(), '2026-01'),
25-
SHOPIFY_PUBLIC_STOREFRONT_TOKEN: v.optional(v.string()),
18+
// Shopify Storefront API token — server-only. Cart reads and mutations
19+
// run through createServerFn (src/utils/shop.functions.ts), so this
20+
// token never reaches the browser. Store domain + API version are
21+
// public-by-design and hard-coded in src/server/shopify/fetch.ts.
2622
SHOPIFY_PRIVATE_STOREFRONT_TOKEN: v.optional(v.string()),
2723
})
2824

0 commit comments

Comments
 (0)