DEV Community

StackOps AI
StackOps AI

Posted on

How We Migrated an AWS Amplify GraphQL Backend to CDK (Without a Rewrite)


AWS Amplify is excellent for getting started with a GraphQL backend.
It removes friction, scaffolds infrastructure quickly, and lets small teams move fast.
But as systems grow, many teams reach a point where Amplify becomes a constraint rather than an accelerator.
In this post, I'll walk you through how we migrated an existing AWS Amplify AppSync backend to AWS CDK without rewriting business logic, why we did it, and what we learned along the way.
This is not an anti-Amplify post. It's about understanding where Amplify fits - and where it stops scaling.

When Amplify Works Well

Amplify is a great choice when:

  • You're early in a project
  • You want fast schema-driven development
  • You're happy with Amplify-managed CloudFormation
  • You don't need fine-grained IAM or custom pipelines

We used Amplify exactly this way at the start.
Using directives like @model, @auth, and connections, Amplify generated:

  • An AppSync GraphQL API
  • DynamoDB tables
  • VTL resolvers
  • IAM roles
  • Lambda data sources where needed

For a long time, this worked well.

The Problems We Eventually Hit

As the backend grew, a few issues became increasingly hard to ignore.

1. Limited control and visibility

Amplify abstracts away a lot of infrastructure:

  • IAM permissions are auto-generated
  • CloudFormation stacks are hidden
  • Resource naming is opaque
    This makes:

  • Code reviews harder

  • Security reviews painful

  • Debugging deployments difficult

2. Difficult multi-environment and platform integration

We wanted:

  • Explicit dev / staging / prod environments
  • Integration with an existing CDK-based platform
  • Predictable diffs and rollbacks

Amplify’s CLI-driven workflow didn’t fit well with this.

3. A hard CloudFormation scaling limit

This was the real forcing function. AWS CloudFormation enforces a hard limit of 500 resources per stack.

Amplify deploys most GraphQL backends into one or a very small number of CloudFormation stacks. As schemas grow, the number of generated resources grows quickly:

  • Resolvers
  • Functions
  • IAM roles and policies
  • DynamoDB tables and GSIs

Once you approach that 500-resource limit per stack:

  • Deployments become fragile
  • Adding new models or resolvers can fail
  • There is no supported way in Amplify to split or refactor the generated stacks

At that point, backend evolution effectively stalls.

Why We Didn’t Rewrite Everything

By the time we hit these limits, we already had:

  • A large GraphQL schema
  • Custom VTL resolvers
  • Lambda-based business logic
  • Production data in DynamoDB
    A full rewrite would have been:

  • Risky

  • Time-consuming

  • Unnecessary
    Instead, we asked a different question:

What if Amplify was only used to generate the initial artifacts — and not to own the backend forever?

Step 1: Use Amplify Once — as a Scaffolding Tool

The core idea is that Amplify can remain your compiler, while CDK becomes your deployment engine. We treated Amplify as a generator, not a long-term platform.

Using Amplify, we generated:

  • schema.graphql
  • .vtl resolver templates
  • Build artifacts:
  • — build/cloudformation-template.json
  • — build/stacks/*.json
  • — build/resolvers/*.vtl
  • Auth logic embedded in resolvers

At this stage, Amplify did its job well.

Step 2: Extract the Durable Assets

From the Amplify backend output, we extracted only what was durable and valuable:

  • GraphQL schema
  • Resolver templates
  • Table definitions
  • Auth rules and logic
    We explicitly did not keep:

  • Amplify CLI

  • Amplify Console

  • Auto-generated CloudFormation stacks

Those are implementation details — not architecture.

Step 3: Rebuild Explicitly in AWS CDK

Each Amplify-generated resource was reimplemented explicitly in AWS CDK. CDK takes over:

  • AppSync API + data sources
  • FunctionConfigurations
  • Pipeline resolvers
  • IAM roles/policies
  • DynamoDB tables (optional)
  • Lambda data sources (optional)

Instead of deploying Amplify stacks directly, we extract the intent into YAML and redeploy from there.This removed Amplify “magic” and made behaviour predictable.

Step 4: Split the Backend into Multiple CDK Stacks

Unlike Amplify’s monolithic stacks, CDK allowed us to:

  • Split AppSync, DynamoDB, Lambda, and IAM into separate stacks
  • Control resource boundaries
  • Avoid CloudFormation’s 500-resource limit entirely

This single change removed a major long-term scalability risk.

Step 5: Config-Driven, Environment-Aware Design

We replaced CLI-driven configuration with:

  • YAML-based config files
  • Environment-specific definitions (dev, staging, prod)
  • Deterministic naming
  • Reviewable diffs via cdk diff Deployments now look like: cdk diff -c env=staging cdk deploy -c env=staging

CDK became the only source of truth.

Step 6: Remove Amplify Completely

Once parity was verified:

  • Amplify project was removed
  • Amplify Console disconnected
  • No more amplify push
    From that point on:

  • Infrastructure changes are code-reviewed

  • Deployments are predictable

  • Scaling is no longer capped by stack limits

What We Learned

  • Amplify is excellent for scaffolding
  • It is not designed for large, long-lived backends
  • The CloudFormation 500-resource limit is a real constraint
  • Migration is safer than rewriting
  • Explicit CDK ownership pays off quickly at scale

Want the Full Setup?

We packaged this approach into a reusable bundle that includes:

  • A production-grade CDK AppSync backend
  • Config-driven resolver and Lambda wiring
  • Migration checklist and hard-earned lessons
  • Real-world examples (not toy demos)

👉 Gumroad: https://stackopsai.gumroad.com/l/eizwk

Final Thoughts

This migration wasn’t about rejecting Amplify. It was about using the right tool at the right stage.

Amplify helped us move fast early. CDK helped us move safely at scale.

If you’re approaching the same limits, there is a clean exit — without a rewrite.

Top comments (0)