How We Migrated an AWS Amplify GraphQL Backend to CDK (Without a Rewrite)
Source: Dev.to
How we moved from Amplify to CDK without rewriting business logic, why we did it, and what we learned.
Note: This isn’t an anti‑Amplify post. It’s about understanding where Amplify shines — 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 (when needed)
For a long time, this worked perfectly.
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 & 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 these requirements.
3. A Hard CloudFormation Scaling Limit
AWS CloudFormation enforces a hard limit of 500 resources per stack.
Amplify deploys most GraphQL backends into one (or a very small number of) stacks. As schemas grow, the number of generated resources quickly approaches that limit:
- Resolvers
- Functions
- IAM roles & policies
- DynamoDB tables & GSIs
When you near the 500‑resource limit:
- Deployments become fragile
- Adding new models or resolvers can fail
- Amplify provides no supported way 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?
Migration Steps
Step 1 – Use Amplify Once, as a Scaffolding Tool
Treat Amplify as a compiler, while CDK becomes the deployment engine.
Amplify generated:
schema.graphql.vtlresolver templates- Build artifacts:
build/cloudformation-template.jsonbuild/stacks/*.jsonbuild/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 kept only what was durable and valuable:
- GraphQL schema
- Resolver templates
- Table definitions
- Auth rules & logic
We did not keep:
- Amplify CLI files
- Amplify Console configuration
- 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 CDK. CDK now owns:
- AppSync API + data sources
- Function configurations
- Pipeline resolvers
- IAM roles / policies
- DynamoDB tables (optional)
- Lambda data sources (optional)
Instead of deploying Amplify stacks directly, we extracted the intent into CDK code and redeployed from there. This removed Amplify “magic” and made behavior 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
Now we get reviewable diffs via cdk diff:
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:
- The Amplify project was deleted
- Amplify Console was 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 a full rewrite.
- 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 implementations
- Example CI/CD pipelines
Feel free to open issues or submit PRs if you run into any snags!
bda wiring
- Migration checklist and hard‑earned lessons
- Real‑world examples (not toy demos)
👉 Gumroad:
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.
