The API
3. Connect them
Proxy, scopes, installing the app, and verifying.
You have an app and a deployed API. Four steps left, and the third is the one almost everyone misses.
1. Point the proxy at the deployment
In shopify-app/shopify.app.toml, using the URL Netlify gave you:
1[app_proxy]2url = "https://your-site.netlify.app"3prefix = "apps"4subpath = "admin-api"subpath must match what the theme asks for. It defaults to admin-api, so if
you use anything else you'll change a theme setting in step 4.
⚠️
prefixandsubpathare frozen when the app is installed. Changing them later needs an uninstall and reinstall — a redeploy is not enough. Decide now.
2. Declare the access scopes
Still in the same file. These control what the Admin API will let your operations do:
1[access_scopes]2scopes = "read_products,read_inventory,read_locations,write_customers"Don't guess at these — npm run api:build prints the exact line, worked out from
the # Scopes: headers in your .graphql files:
Access scopes — paste into shopify-app/shopify.app.toml: [access_scopes] scopes = "read_inventory,read_locations,read_products,write_customers"Getting this wrong doesn't fail at setup. It fails later, at the Admin API, with an access-denied error that names neither the missing scope nor the operation that needed it.
3. Deploy the app, then install it
cd shopify-appnpx shopify app deployThat publishes the configuration — the proxy path, the scopes — to Shopify.
And that is not enough. Deploying tells Shopify what the app is. It does
not put it on your store. Until the app is installed, the proxy doesn't exist
for that storefront, and your-store.com/apps/admin-api/admin.gql returns a bare
404 that never reaches Netlify.
npx shopify app dev --store your-store.myshopify.comThat prints an install link — open it and accept the scopes. In the Dev Dashboard the same thing lives under your app → Test on development store.
Check where you stand at any time:
npx shopify app infoDev store Not yet configured ← not installed anywhere; 404 is expectedAccess scopes read_products,… ← what the app will ask for4. Point the theme at the proxy
The theme's API base defaults to /apps/admin-api, which matches the default
subpath. If those agree, there is nothing to do.
If you chose a different subpath, set it in the theme editor under Theme settings → API.
A mismatch here produces a 404 from Shopify that never reaches your deployment — so the API looks broken when it is only mounted somewhere else.
5. Verify end to end
From your storefront — the browser console on any page will do:
1const { shop } = await admin('GetShop')2console.log(shop.name)Your store's name comes back. That request went storefront → Shopify → App Proxy → Netlify → Admin API and back, signed the whole way, with no credential ever in the browser.
When it doesn't work
Work down this list in order — each rules out the one below it.
| What you see | What it means |
|---|---|
404 on /apps/admin-api/… | The app isn't installed. shopify app info → Dev store Not yet configured |
404 after changing subpath | The path was frozen at install. Uninstall and reinstall |
500 Server is not configured | SHOPIFY_API_SECRET missing on Netlify, or set but not redeployed |
401 Invalid signature from the storefront | The secret on Netlify isn't the app's real Client secret |
401 Invalid signature from curl | Expected — a direct request has no signature |
Could not obtain an Admin API token | Wrong client id/secret, or app and store in different organizations |
| Access-denied from the Admin API | A missing scope. Add it, shopify app deploy, reinstall |
| A new operation isn't found | Not compiled in yet. npm run api:build, then redeploy |
The deployment logs carry Shopify's own message for token failures, which is usually more specific than anything the gateway can say without leaking credentials to the browser.
The whole picture
Storefront Shopify Netlify ────────── ─────── ─────── admin('GetShop') │ └──▶ /apps/admin-api/admin.gql │ │ verifies the request came │ from this shop, signs it ▼ POST /admin.gql ──────────────▶ verify signature (client secret) │ look up "GetShop" in the allowlist │ exchange id+secret for a 24h token │ ▼ Shopify Admin APIThe browser names an operation. The server owns the query text, the credentials and the destination. That's what makes it safe to expose to a storefront at all — see why it takes a name.