Back to Blog
AWS

Moving from Strapi to AWS Amplify Gen 2 to Learn a New Backend

Strapi was a good CMS for Terry Hunt Dev. I began moving the backend to Amplify Gen 2 because I wanted to learn a different way to model, secure, and deploy application data.

Terry Hunt
August 24, 2026
6 min read

Strapi did its job on Terry Hunt Dev. It gave me structured content, an admin panel, and a clean API for Next.js. I began moving the site on an Amplify Gen 2 migration branch because I wanted to learn through a real application instead of another todo demo. The branch now contains a TypeScript backend definition, AppSync data access, and Cognito-backed administration. The repository still lists production schema deployment and hosted admin CRUD verification as remaining work, so this is a migration-in-progress case study rather than proof of a completed production cutover.

What I was giving up

The existing CMS already handled the work I would otherwise have to rebuild. I could define collection types, reusable components, relations, media fields, and editorial permissions without building each screen myself. The Strapi Content-type Builder documentation shows the same content-modeling tools that made the original backend practical.

Draft & Publish was useful because an editor could save work without exposing it on the public site. Strapi still documents that workflow in its Strapi Draft & Publish documentation. I did not leave because the CMS failed or because I thought it had become obsolete.

I wanted the learning project to be real

Small tutorials hide the decisions that make backend work difficult. The Strapi seed importer covers blog posts, projects, landing pages, location pages, services, and SEO overrides. The Amplify schema also contains SiteSettings, MarketingCampaign, SeoPage, and Contact models, but those four are populated through separate workflows rather than this Strapi importer. Working through the imported collections forced me to handle authorization, publishing state, stable URLs, and failed imports.

Amplify Gen 2 defines backend resources in TypeScript. The Amplify Data setup guide covers schema definition, generated clients, and authorization rules. I wanted experience with that model and the AWS services behind it, so I used my own site as the test case.

I mapped the content before changing the API

I started with a field inventory rather than translating Strapi responses inside page components. Each Strapi collection mapped to an Amplify model or a deliberate consolidation. Slugs, canonical URLs, publication dates, robots directives, SEO fields, and structured data needed explicit destinations.

Relations and components forced a choice between normalized Amplify models and JSON stored as one page section. I used JSON when the frontend consumes the structure as one array. Frequently queried fields such as blog slugs and categories became secondary indexes. This was a new data model built from the old editorial requirements, not a mechanical rename.

The Amplify schema became part of the repository

The current amplify/data/resource.ts defines ten models, including BlogPost, Project, LandingPage, and Contact. Public content models allow API-key reads while the Admin Cognito group controls writes. Contact records use a narrower rule because public visitors should not read submissions.

Keeping the schema beside the application made backend changes visible in code review. It also meant I had to design the admin experience, publication rules, and field validation that Strapi had supplied through its CMS interface.

I used an importer I could run more than once

The migration script reads the exported Strapi seed data, maps each record, and sends GraphQL mutations to Amplify Data. Stable IDs based on slugs let the script find existing records. By default it creates missing records and skips existing ones; an authenticated Admin token is required when I intentionally update data.

I kept the transformations explicit. Arrays are coerced to string arrays, selected media objects are reduced to URL or path values, and JSON values are serialized for AppSync JSON inputs. Missing publication dates receive a current ISO timestamp, but existing date strings pass through unchanged. Strapi also provides official Strapi data transfer documentation, but a cross-platform move still needs a field map because the destination schema and authorization model differ.

The frontend moved after the data

The repository's public data loaders now use Amplify and AppSync, and the importer uses slug-derived IDs while logging created, updated, and skipped totals for each collection. The repository does not retain a complete source-versus-destination reconciliation report, so I do not claim count parity from code alone. It also includes custom /admin routes for Admin-group Cognito users, although the deployment checklist still marks hosted CRUD verification incomplete.

I check rendered pages as well as API responses because titles, canonicals, robots directives, images, structured data, sitemaps, and draft visibility can regress while a record looks correct in GraphQL. Tracked records from content/blog-drafts import as draft and noindex,follow. Other imported records use their source publishStatus, and several mappers default a missing value to published, so pending editorial state must be explicit in the source data. The current public API-key read rule does not make drafts private; the rendered site filters unpublished records, and drafts cannot contain nonpublic material until the API boundary enforces that distinction.

The migration moved CMS work into my codebase

Amplify Gen 2 brought the backend definition, authorization rules, and deployment configuration into the same TypeScript project as the site. That was the learning goal. I now configure AppSync access, Cognito groups, generated outputs, branch environments, and request permissions directly.

Strapi remains the stronger fit when a team wants its ready-made content manager, media library, plugin options, and editorial workflow. My Amplify version uses a custom admin and repository-specific import tools. Those are useful for this application, but they are code I must maintain.

I kept the old bridge during verification

The repository still names the temporary import command import:strapi-seed, and a few runtime error classes retain Strapi-oriented names. I left those clues in place while verification and cleanup continued. Removing the bridge too early would make a missed record harder to recover.

The final cleanup includes rotating old Strapi tokens, deleting obsolete secrets, renaming migration-only code, and preserving a separate export for audit and rollback. My Strapi hosting guide covers the operating model I left, while Amplify vs. Vercel for Next.js explains the hosting tradeoffs around the new stack.

Choosing between Strapi and Amplify Gen 2?

I still recommend Strapi when a project needs a mature editor-first CMS. For an AWS-centered application, I can help map the content and authorization requirements before any data moves. Share a sanitized model inventory through the contact page, or see how I work with Strapi and AWS.

Related Articles

More articles in AWS

Enjoyed This Article?

Get more tutorials and insights delivered to your inbox. No spam, just valuable content for web developers.