Tutorials

How to Migrate from Strapi to Decoupled.io

Jay Callicott··8 min read

How to Migrate from Strapi to Decoupled.io

Strapi is one of the most popular open-source headless CMS options, and its appeal is obvious: it's free to self-host, it's built on Node.js (a stack most frontend teams already know), and the content type builder is genuinely easy to use. But self-hosting comes with costs that aren't on the invoice — server management, database backups, security patches, scaling, and uptime monitoring.

If you've been running Strapi in production and the DevOps overhead is wearing thin, here's how to migrate to Decoupled.io and get a managed CMS with a better API, a visual page builder, and no servers to maintain.

Why Migrate

Self-hosting is a full-time job. Running Strapi means managing Node.js processes, PostgreSQL or MySQL databases, file storage (local or S3), reverse proxies, SSL certificates, and deployments. When your Strapi instance goes down at 2 AM, that's your problem. Decoupled.io is fully managed — infrastructure, backups, scaling, and security are handled for you.

The API layer is bare-bones. Strapi gives you REST and GraphQL out of the box, but both are generic. REST responses include metadata you have to parse through. GraphQL works, but you're writing queries by hand and maintaining your own types. Decoupled.io's typed client auto-generates TypeScript types from your schema — no hand-written queries, full IDE autocomplete.

No visual page builder. Strapi's admin panel is great for structured data entry, but it doesn't have a page builder. If your content editors need to assemble landing pages from sections — hero, feature grid, testimonials, CTA — you have to build custom components in Strapi and corresponding rendering logic in your frontend. Decoupled.io includes a visual page builder with Paragraphs that editors can assemble without developer help.

What You'll Need

  • A Decoupled.io account
  • Access to your Strapi admin panel and database
  • Your frontend codebase
  • About 2-4 hours for a typical migration

Step-by-Step Migration Plan

Step 1: Export Your Strapi Content Types and Data

Strapi stores content type definitions as JSON files in your project's src/api/ directory. Review these to understand your content model. For the actual content, use Strapi's REST API to export data:

# Export all entries for a content type
curl http://localhost:1337/api/articles?pagination[pageSize]=100 \
  -H "Authorization: Bearer YOUR_API_TOKEN" \
  > articles.json

Repeat for each content type. For media files, you'll need access to your uploads directory or S3 bucket.

Step 2: Create Matching Content Types in Decoupled.io

Map your Strapi content types to Drupal content types in the Decoupled.io dashboard:

Strapi Decoupled.io (Drupal)
Collection Type Content Type
Single Type Content Type (single-use)
Component Paragraph type
Dynamic Zone Paragraphs field
Relation Entity Reference
Media field Media reference
Enumeration List (text) field
UID (slug) URL alias (automatic)

Strapi's "Dynamic Zones" — fields that accept multiple component types — map directly to Drupal's Paragraphs system. This is actually an upgrade: Drupal Paragraphs include a visual builder interface that Dynamic Zones don't have.

Step 3: Import Content via Migration Scripts

Write a migration script that reads your exported JSON and creates corresponding content in Decoupled.io via the API. Drupal's Migrate API is designed for exactly this kind of structured import.

For each content type:

  1. Map Strapi fields to Drupal fields
  2. Handle relations (create referenced content first, then resolve references)
  3. Download and import media files as Drupal media entities

Step 4: Generate the Typed Client

Once your content is in Decoupled.io, generate the typed client:

npx decoupled-cli@latest schema sync

This introspects your Drupal schema and generates schema/client.ts with TypeScript interfaces for every content type and pre-built queries for fetching and listing.

Step 5: Update Your Frontend Data Layer

Replace your Strapi API calls with typed client calls. The pattern is similar — you're still fetching structured content from an API — but the developer experience is better.

What Changes in Your Frontend Code

Here's what the data-fetching layer looks like before and after:

Before (Strapi REST API):

const res = await fetch(
  'http://localhost:1337/api/articles?populate=*&sort=publishedAt:desc&pagination[limit]=10',
  { headers: { Authorization: `Bearer ${process.env.STRAPI_TOKEN}` } }
)
const { data } = await res.json()

// Strapi wraps everything in { data: { id, attributes: { ... } } }
const title = data[0]?.attributes?.title
const image = data[0]?.attributes?.image?.data?.attributes?.url

After (decoupled-client):

import { createClient } from 'decoupled-client'
import { createTypedClient } from './schema/client'

const base = createClient({
  baseUrl: process.env.NEXT_PUBLIC_DRUPAL_BASE_URL!,
  clientId: process.env.DRUPAL_CLIENT_ID!,
  clientSecret: process.env.DRUPAL_CLIENT_SECRET!,
})

const client = createTypedClient(base)

const articles = await client.getEntries('NodeArticle', {
  first: 10,
  sortKey: 'CREATED_AT',
  reverse: true,
})

// Clean, typed access — no .attributes wrapper, no .data nesting
const title = articles[0].title
const image = articles[0].image?.url

No more data[0].attributes.field.data.attributes.value chains. No more ?populate=* to get related content. The typed client returns clean objects with types that match your schema.

What Stays the Same

  • Content modeling — You still define content types with fields, components, and relations. Drupal's content modeling is more mature (built-in revisions, content moderation, multilingual support), but the concepts are the same as Strapi's content type builder.
  • API-first architecture — Your frontend still fetches content from an API. The data source changes, but the architecture doesn't.
  • Editorial workflow — Content editors create, edit, and publish content through a web-based admin panel. Drupal's admin interface is different from Strapi's, but the workflow is familiar.
  • Media handling — Images and files are uploaded through the admin panel with automatic optimization. CDN delivery is included with Decoupled.io's managed hosting.

Next Steps

  1. Get started with Decoupled.io — Create your account and set up your first managed Drupal space.
  2. See how Decoupled.io compares to Strapi — Feature comparison including hosting, API, and content modeling.
  3. Explore pricing — Managed hosting that costs less than running your own infrastructure.
  4. Learn the typed client — Full documentation for decoupled-client, including sorting, filtering, and pagination.