Mastering Your Shopify App Migration: From PHP/REST to FastAPI/GraphQL & App Bridge

Hey everyone! I've been diving deep into the Shopify community forums lately, and a recent thread really caught my eye. It's a common challenge we see: modernizing a legacy Shopify custom app. Annex_Cloud (Jitesh Sinha) kicked off a fantastic discussion, asking for guidance on moving from a PHP 7.1, REST API, plain OAuth setup to a cutting-edge FastAPI, GraphQL, App Bridge architecture. This isn't just a tech upgrade; it's about aligning with Shopify's future, reducing technical debt, and building a more scalable, robust loyalty platform.

The community really stepped up, offering some gold-standard advice that I wanted to distill for all of you grappling with similar migrations. Let's break down the key takeaways from TinyOpsStudio and dataease, who shared invaluable production-hardened insights.

The Six Critical Migration Boundaries

TinyOpsStudio laid out a brilliant framework, suggesting we treat the migration as six explicit boundaries. This approach helps you tackle the complexity piece by piece, ensuring a smoother transition:

1. Embedded UI & App Bridge

First off, you'll need to load the latest App Bridge CDN script. The big shift here is obtaining a fresh App Bridge ID token (what used to be called a 'session token') for every browser-to-FastAPI request. This short-lived JWT is crucial for secure communication within the embedded app.

2. Backend Request Authentication

Your FastAPI backend needs to be vigilant. You'll need to verify the HS256 signature of those ID tokens, along with claims like exp, nbf, aud, iss, and dest before trusting any request. For background jobs and webhooks, exchange that ID token for an offline access token. Only use online tokens when an action absolutely needs to respect the current staff member's permissions or attribution.

3. Installation & Scopes

This is where you embrace modern Shopify. Move your app's scopes and subscriptions into the shopify.app.toml file and leverage Shopify-managed installation. Store tokens securely per shop, ensuring you also record the granted scopes and the API version. Ditch your custom OAuth state machine; the token exchange process should cover your embedded flow.

4. The GraphQL Boundary

This is a major architectural shift. Shopify is marking the REST Admin API as legacy, so you really want to avoid carrying REST-shaped models into your new core. Instead:

  • Put GraphQL operations behind domain services (e.g., customers, orders, rewards).
  • Pin an API version.
  • Centralize your cost and throttle handling.
  • Use cursor pagination, not offset.
  • Migrate one resource group at a time.

On the topic of rate limits, dataease highlighted a critical difference: REST gives you a request bucket, but GraphQL charges a calculated query cost against a leaky bucket. This means you get points per second (100 on Standard, 1000 on Plus), and every response includes a throttleStatus in its extensions block with currentlyAvailable and restoreRate. You absolutely should drive your client's retry logic off that, not a fixed sleep! And here's a new habit to learn: asking for fewer fields genuinely costs less with GraphQL.

Here's a visual reminder of the Shopify API limits:

Shopify API Rate Limits illustration

5. Webhook Ingress

When webhooks come in, first verify the HMAC against the raw request body before JSON parsing. Deduplicate using the X-Shopify-Webhook-Id header. Return a 2xx status quickly and queue the actual work. Make sure your consumers are idempotent, and set up a periodic GraphQL reconciliation job for any missed or delayed events. Always keep Shopify as the source of truth for customer and order facts, and your loyalty platform for points and rewards. Correlation IDs and an outbox pattern are your friends here to prevent double redemptions.

6. Data & Compliance

Before requesting any scopes, inventory every customer and order field you currently use. Customer and order data is protected, so your uninstall, data-request, and redaction flows must be part of your migration test plan.

Pro Tips from the Trenches

Beyond the structural changes, dataease shared some crucial production lessons:

Bulk Operations for Backfills

For initial data backfills (like loyalty data for customers and orders), do not paginate! This is huge. Instead, use a bulk operation. The bulkOperationRunQuery will hand you a JSONL URL, and you can subscribe to the bulk_operations/finish webhook instead of polling. This is far more efficient and less prone to hitting rate limits.

Shopify GraphQL Admin API Bulk Operations illustration

Smart Error Handling

Build your error taxonomy early. Status codes like 500, 502, 503, and 504 are generally worth retrying with an exponential backoff. However, a 402 means a frozen shop and is permanent — retrying it just wastes worker capacity. Learn this one the easy way, not the slow way like some of us did!

Dev Store Gotcha

Here's a tricky one: if you change scopes in your app config, the old grant often stays cached on your development store. To avoid chasing a phantom re-auth loop that isn't in your code, always uninstall and reinstall your app on the test store after changing scopes.

Answering Annex_Cloud's Specific Questions

Annex_Cloud asked some really pertinent questions, and the community's insights provide clear answers:

  • Recommended authentication flow today: Use App Bridge Session Tokens for embedded UI interactions and Admin API Access Tokens (offline) for backend processes and webhooks.
  • When to use App Bridge Session Tokens vs. Admin API Access Tokens: Session tokens are for verifying browser requests within the embedded app, ensuring the current staff member has permission. Admin API access tokens (especially offline ones) are for making API calls from your backend, often for background jobs or responding to webhooks.
  • How Fast API should validate Shopify session tokens: FastAPI should verify the HS256 signature and validate claims like exp, nbf, aud, iss, and dest, as mentioned in the backend authentication boundary.
  • Reference implementations in Python/Fast API: Yes! Shopify now has an official shopify-app-python package with a FastAPI quickstart, which is an excellent starting point.
  • Would you recommend FastAPI for Shopify app development? Absolutely. Given Shopify's official support and the package, FastAPI is a very reasonable and recommended choice.
  • Which libraries, SDKs, and frameworks are commonly used today? The Shopify/shopify-app-python repository is the primary reference.

A Practical Cutover Sequence

TinyOpsStudio also provided a sensible sequence for the actual cutover:

  1. Catalog all your current REST calls and webhooks.
  2. Add contract tests around your loyalty connector.
  3. Build GraphQL read parity (make sure your new system can read everything the old one could).
  4. Introduce the new webhook queue and reconciliation job.
  5. Move the embedded UI and token exchange to the new system.
  6. Migrate writes resource by resource.

Crucially, run the old and new read paths side by side for long enough to reconcile counts and IDs before each cutover. This parallel running is key to catching discrepancies early.

Modernizing a legacy app is no small feat, but with these clear boundaries, expert tips, and the right tools like FastAPI and Shopify's official Python package, it becomes a much more manageable and rewarding journey. For merchants looking to enhance their operations or build a new custom solution, choosing a robust platform like Shopify provides the foundation for powerful app integrations. The insights from this community discussion are a testament to the power of shared experience in navigating complex technical transformations. Good luck with your migrations!

Share:

Start with the tools

Explore migration tools

See options, compare methods, and pick the path that fits your store.

Explore migration tools