Add proxy routes
OpenShop discovers route handlers in proxy/ and mounts each one under both
/proxy/* and /ext/*.
1. Create a route
Section titled “1. Create a route”Create proxy/api/reviews.ts:
import { app } from '#app'
export default app.defineProxy({ type: 'json',
async GET({ shop, customerId, query }) { return { shop, customerId, page: Number(query.page) || 1 } },
async POST({ shop, customerId, body }) { if (!customerId) return { ok: false, error: 'Customer login required' } return { ok: true, shop, customerId, review: body } },})Supported handlers are GET, POST, PUT, DELETE, and PATCH. Non-GET
bodies are parsed as JSON; invalid JSON becomes undefined.
2. Check the generated paths
Section titled “2. Check the generated paths”proxy/index.ts -> /proxy and /extproxy/reviews.ts -> /proxy/reviews and /ext/reviewsproxy/products/[id].ts -> /proxy/products/:id and /ext/products/:idPrefix helper files or directories with _:
Directoryproxy/
Directoryloyalty/
- _service.ts
- _queries.ts
Directory_shared/
- index.ts
Only .ts and .js route files are loaded.
3. Configure the Shopify app proxy
Section titled “3. Configure the Shopify app proxy”In the Shopify app configuration, point the app proxy destination at the public
OpenShop /proxy/... route. Shopify owns the storefront prefix, subpath, and
TOML schema; use the fields supported by your Shopify CLI version.
Deploy the configuration:
shopify app deploy --config shopify.app.tomlAdding a file under proxy/ does not register the app proxy in Shopify.
4. Call from a Customer Account extension
Section titled “4. Call from a Customer Account extension”Enable direct network access in the extension TOML:
[extensions.capabilities]network_access = trueCall the /ext mount on your app origin:
const token = await shopify.sessionToken.get()
const response = await fetch(`${apiOrigin}/ext/api/reviews`, { headers: { Authorization: `Bearer ${token}`, },})/ext/* requires a valid Customer Account session JWT. /proxy/* accepts that
JWT too, but /ext avoids Shopify CLI proxy interception.
5. Verify authentication and CORS
Section titled “5. Verify authentication and CORS”For storefront app proxy traffic, Shopify signs the query and OpenShop verifies it against exactly one configured app secret. For extension traffic, OpenShop verifies the JWT audience and signature.
Trust ctx.shop, ctx.shopifyApp, and ctx.customerId; do not accept identity
from unsanitized client fields. A missing/invalid credential returns HTTP 401.
Shopify extension, admin, storefront, local, HOST, and SHOPIFY_APP_URL
origins receive CORS headers. Arbitrary origins do not.
Handler exceptions are logged and return
{ "error": "Internal proxy error" } with HTTP 500. A successful handler value
is always HTTP 200; the current proxy API does not expose custom status or
response headers.