
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)