Decoupled Platforms: Contentful vs. Drupal

Two mail slots in a door

Many platforms are advertising the ability to manage content in one place while publishing it to multiple channels. They provide a way to model, manage, and create content and then expose that content via an API. 

These are decoupled-only (or headless) back-ends. The only way to present your content is to create an application that consumes the API, interprets it, and renders the content in your desired format.

Contentful is one such platform, and in this article, we’ll be comparing Contentful’s capabilities to Drupal’s capabilities as decoupled content providers. We’ll be using Drupal Core’s JSON: API, which requires no additional configuration to get up and running and is the most popular way for Drupal to serve as a decoupled back-end.

We’ll cover the following:

In the end, we’ll answer the question: can Drupal do anything that Contentful can do? The answer to that is yes, and more, though it takes different paths to get there.

Content Modeling

A good CMS experience starts with content modeling capabilities. Can we accurately represent the data that we need? Can we enable content creation in a well-organized way and defined based on an overarching content strategy?

Like Drupal, Contentful offers a flexible way to create content types and relate them to each other.

Field types

Contentful offers the following field types:

  • Rich text - text formatting with entity embeds.
  • Text - short or long text.
  • Number - integer or decimal.
  • Date and time - can be date only or date with time.
  • Location - latitude and longitude.
  • Media - images, videos, and other files.
  • Boolean
  • JSON object - arbitrary data in JSON format.
  • Reference - reference other content.
Contentful field types

How does Drupal compare to this line-up? Drupal Core contains most of these without needing to install other modules.

Drupal Field Types

Drupal includes a few more format-specific field types, like Email and Link. Contentful doesn’t have these extra field types because it has a “Match a specific pattern” validation you can apply to any text field. You can use a regular expression or one of the supplied patterns (Email, URL, Phone number, etc.)

Contentful field pattern validation

But this is only for immediate validation when creating content. These pattern validations do not change the underlying schema, and there are no distinct data types. As far as any consumer of a Contentful API is concerned, any text field, no matter the pattern validation applied, should be treated as normal text.

You could mimic the same approach in Drupal with the Field Validation module. But there are benefits to how Drupal implements data types (like Email or Link), especially for the back-end. An automated job that checked for broken links, for example, benefits from knowing that something is a link.

Rich text fields include a WYSIWYG editor similar to Drupal, and setting it up is simple and clear. It includes a toggle for most of what you would want to allow: 

  • Headings (H1 through H6)
  • Bold, italic, and underline buttons
  • Basic code formatting
  • Blockquotes
  • Hyperlinks
  • Ordered and unordered lists
  • Embeds of media or other content
Contentful Rich Text Settings
Contentful's Rich Text Field Settings

What is Drupal Core missing in terms of field types and modeling?

  1. Location - This gap can be filled with the Geofield module.
  2. JSON object - For Contentful, this is the field type you can use to inject whatever data you want into a content type, and there isn’t a corresponding field type that matches what you need. Of course, it’s not very user-friendly for editors. It is very likely the Drupal landscape has the module you need for the data you want to store, but there is also the JSON Field module if you want to mimic the field type itself.

Contentful has one way of doing something, like how to include images and other media. And it works well for the 80%.

With Drupal, there are different ways to do this, even with just what is included in Drupal Core. You have to make a decision upfront, and if you want to switch later, it could be a huge development effort. You could use normal file fields and the basic image embedding offered by the built-in CKEditor.

Or you could enable the Media and Media Library modules, which will use a different type of field and a different way to embed media into rich text. And your entire paradigm changes.

Contentful’s biased approach saves you some brain cycles in figuring out how you want to accomplish some things. It just works. Drupal requires more planning and setup at the beginning. This will be a common theme for other areas of comparison as well.

What is Contentful missing in terms of field types and modeling?

The easiest way to answer this is to just point to the Drupal module ecosystem. Since it is open-source, there are a wide variety of contributions, and since Drupal encourages compatibility, many of these modules work together in interesting ways. 

However, this huge library can also be Drupal’s weakness. It can be hard to find what you are looking for if you don’t already know the correct terminology, and it tends to be less polished. There are sometimes multiple ways of doing something, and unless you have been involved in Drupal for years, you don’t know the “best” way.

Analysis paralysis.

We ran into this same problem while writing this article. What exactly do we include in this section beyond waving our hands in the direction of Drupal’s module directory?

In the end, we landed on some “must-haves” based on common usage, following the 80/20 rule.

  • Multi-value fields - Other than for its Reference and text fields, Contentful does not provide multi-value fields like Drupal. If you want multiple values for a number or location, you need to create multiple fields. That can get cumbersome for both content type setup and the eventual rendering of the content by a consumer. Grouping the related values would require naming all the fields a certain way, which leads to fragility. Drupal’s way of handling this type of data provides clarity and intention.
  • Physical addresses - Drupal’s Address module is a robust modeling of complicated data that differs from one geographic area to the next. Contentful requires you to build up an address field from multiple text fields or write a custom extension.
  • Extensible rich text editor - Drupal uses the mature CKEditor (though you can switch this out if you want), and plugins can be added to extend its functionality. Contentful’s Rich Text editor cannot be modified, though you can take their code for it and modify it to your needs. That means you miss out on any updates and improvements they might push.
Drupal CKEditor Setup and settings
Drupal's Rich Text Settings

Drupal’s Entity structure is also an improvement over Contentful’s entity structure. 

Contentful only has “Content” that you can model. In Drupal, Users, Media, Taxonomy Terms, and much more are entities that can have fields and complex modeling. With Users in particular, this adds some exciting possibilities for things like advanced access control, workflows, and personalization. 

Some of this will simply not be possible in Contentful.

Drupal went through its “everything is content” phase and came out the other side as a mature tool capable of modeling complex domains and requirements. It paid for this maturity with blood, sweat, and tears. Previous versions have the scars to prove it. 

Contentful will also have to learn similar lessons as it matures.

Editorial Experience

How easy is it to find, create, and manage content? Contentful’s default editorial experience looks great and is easy to use. Everything feels fluid and natural. This is one of the natural benefits of a SaaS product, as they have a given feature set and don’t need to be able to solve every editorial problem imaginable.

But this just pertains to the default Contentful Web App. 

If your team is familiar with React, you can customize the editorial experience at the level of a page, content type, field, sidebar, and dialogs. But then you lose some of the benefits of Contentful’s sensible defaults.

With some “locations,” you need to implement your own error handling, rich text data entry, and closely tracking release updates to ensure the underlying data structure has not changed. You are looking at the same development effort as a complex, medium-sized React application. Field and widget customizations have the complexity of writing an individual React component or subcomponent. The good news is that Contentful has released their field editors as open-source, so you have some guidance and/or things you can reuse when writing your own.

This is something you should keep in mind when evaluating Contentful, especially if you feel you will need unique customizations for how you manage content.

Creating content

Each field type has a good selection of widgets to choose from: text fields, drop-downs, radio buttons, URL formatters, and more. Contentful number fields even have the option to enter them as stars to mimic review sites. Long text fields (that are not rich text) have the option for Markdown entry.

Contentful Text Widgets
Contentful Number Widgets
Contentful Long Text Widgets

You can add existing references or create a piece of content at the same time as referencing it. This can also be done in the context of a rich text field to embed content. It feels intuitive.

Contentful Insert Existing Reference

Content views

The default content view lists all content in the system, with easy ways to filter that list of content. You can filter by content type and the value of any field, including reference fields. 

Contentful content overview

You can save any combination of filters as its own view for quick access later. This view can be saved as a “shared view” for everyone or one for your own use. This is incredibly helpful, as it gives editors more autonomy. It’s as easy as typing in a name and clicking “save.”

Drupal's Default Content View
Drupal's Default Content View

Translations

Contentful allows you to add locales for content translation. You can then mark fields as translatable. Their default editorial app allows you to switch between locales when creating or editing content, displaying the proper fields.

If you want more than 10 locales, however, you will need their Enterprise tier. In addition, there is no way to translate the default editorial interface. 

If you want a localized experience for your editors, you will need to write your own editorial app.

What is Drupal Core missing in terms of the editorial experience?

  • Duplicating content - This gap can be filled with the Entity Clone module.
  • Entity usage - This gap can be filled with the Entity Usage module, though this can present some scalability issues with some setups.
  • Scheduling content - This gap can be filled with the Scheduler module.
  • Search or create an entity to reference - With a combination of Embed, Entity Embed, Entity Browser, and Inline Entity Form, you can reach functional parity with Contentful’s referencing experience. It takes some effort to set up, but it can be done without any custom code. See images below.
  • Revision comparison and restoration - This gap can be partially filled with the Diff module. However, the out-of-the-box interface is not as nice or intuitive as Contentful’s, which allows for easy field-level restoration. It is straightforward to view differences at a glance and roll back only the fields you want. Drupal has full revision rollback capabilities but is less polished.

The ability for editors to save their own views is useful and not something Drupal can do easily. Any new View, because it is stored in configuration, will require some developer time and a deployment.

Drupal media embedding
The pop-up window for Drupal's media embedding
Drupal's inline entity form in action
Drupal's Inline Entity Form in action to create entities and reference them at the same time.

What is Contentful missing in terms of the editorial experience?

Drupal has a lot of options for you to craft a good experience for your editors. While many things will not look as sleek or polished as Contentful (a problem being tackled by the Admin UI & Javascript Modernisation Initiative), you can get close. 

Not only are there administration themes looking to fill some gaps, like Gin, there is also plenty you can get done in Drupal through clicking some buttons in the user interface. Doing something similar in Contentful would mean custom development of a React application or component.

  • Form organization - The Field Group module provides custom grouping of fields that allow easy customizations of the editorial experience. This is useful for content types with a lot of fields. With Contentful, you must have a fully custom “Entry Editor.”
  • Multi-value fields - This was also a line-item for content modeling, but it also applies to the editorial experience. There are no multi-value fields for some of their data types in Contentful, so you will not be able to have a widget with checkboxes on many of the field types. Not without heavy customization, at least.
  • User roles and permissions - Drupal gives you unlimited granularity in the roles you define. Contentful limits the roles you can have. The first paid tier gives you some more predefined roles, but you must have an Enterprise-tier account to have custom roles and permissions. 
  • Editorial workflows - Drupal Core includes Content Moderation module and the Workflows module. With them, you can define an unlimited amount of arbitrary publishing states and workflows. No matter how large or varied your team, you can map your ideal workflow and implement it. Because of Contentful’s limited roles, advanced workflows are not possible unless you upgrade to the Enterprise tier.
  • Advanced views - While editors cannot easily create their own content views, Drupal’s View system can create more varied experiences, with the ability to add any field to the view, pull in relationships, and perform bulk operations. If you have a development team working on the back-end anyway, it is effortless to roll out new content views based on particular needs.
  • Translations - Drupal provides unlimited locales. You can also translate the editorial interface without any special customization and without rewriting the whole thing from scratch. Translation workflows can also be combined with more advanced editorial workflows, so Drupal translations can fit within the way your editors want to work.

More on multi-value fields. No doubt Contentful would recommend you create a new content type for anything that needs to be multi-valued. That way, editors can add one to many references to that content, and more, that content can be re-used. It helps things stay organized.

But that seems like overkill for something that just needs a single field of data, and if you need more than 48 content types, you will need the Enterprise tier. You will then be charged accordingly for the number of content types that you need.

Drupal’s flexibility, with unlimited content types and multi-value fields, is something to keep in mind.

The API

Here is the bread and butter of what Contentful offers: the content API. We’ll look at how it measures up to the API included in Drupal Core, JSON: API. For this overview, we will only look at the Content Delivery API, though both Contentful and Drupal have ways to create content through an API and ways to call and preview unpublished content. 

For our comparisons, we have created the same content model in both Contentful and Drupal 8, populated with the same data. This gives us a good sampling of data types and organization to make a good comparison. This is a simple music library.

  • Artist - A Name field, an About field that is rich text so we can include embedded entities, a Latest Album reference field that will reference an Album content type, and a Contact field that will hold an email address.
  • Album - A Title field, a Description field, a Cover Art field so we can look at Media assets and management, a Release Date field for a date/time sample, an Artist field that references an Artist content type, and a Tracks reference that references many entities of the Track content type.
  • Track - only a Title field.
Music library site structure

Single Content Queries

This is the API response for the Abbey Road album:

Request format: 

https://cdn.contentful.com/spaces/{space_id}/environments/master/entries/{entry_id}?access_token={access_token}

{
    "sys": {
        "space": {
            "sys": {
                "type": "Link",
                "linkType": "Space",
                "id": "79u36k13bxm5"
            }
        },
        "id": "2oc0DNVizJuFLoXsG7EHwi",
        "type": "Entry",
        "createdAt": "2020-11-12T22:28:11.031Z",
        "updatedAt": "2020-11-12T22:38:16.502Z",
        "environment": {
            "sys": {
                "id": "master",
                "type": "Link",
                "linkType": "Environment"
            }
        },
        "revision": 2,
        "contentType": {
            "sys": {
                "type": "Link",
                "linkType": "ContentType",
                "id": "album"
            }
        },
        "locale": "en-US"
    },
    "fields": {
        "title": "Abbey Road",
        "releaseDate": "1969-09-26",
        "description": "Abbey Road is the eleventh studio album by the English rock band the Beatles",
        "coverArt": {
            "sys": {
                "type": "Link",
                "linkType": "Asset",
                "id": "32z3JMUzfZuUTCv8EtCumB"
            }
        },
        "tracks": [
            {
                "sys": {
                    "type": "Link",
                    "linkType": "Entry",
                    "id": "3TAx53F4EXxz3YVuGJYZql"
                }
            },
            {
                "sys": {
                    "type": "Link",
                    "linkType": "Entry",
                    "id": "26HTJnrAXT3UgQzPuQx5AA"
                }
            },
            {
                "sys": {
                    "type": "Link",
                    "linkType": "Entry",
                    "id": "1F6nqI6J96D7j4KlOQrRvQ"
                }
            },
            {
                "sys": {
                    "type": "Link",
                    "linkType": "Entry",
                    "id": "3veIx6bwvs6EuJz41308Nk"
                }
            },
            {
                "sys": {
                    "type": "Link",
                    "linkType": "Entry",
                    "id": "1Dhy3jECJdyNTYCrQgxQwk"
                }
            }
        ],
        "artist": {
            "sys": {
                "type": "Link",
                "linkType": "Entry",
                "id": "Xm0gwlpQGITlLvcyvTyyM"
            }
        }
    }
}

Here is the same data represented by Drupal’s content API.

Request format: https://example.com/jsonapi/node/{type}/{uuid}

{
    "jsonapi": {
        "version": "1.0",
        "meta": {
            "links": {
                "self": {
                    "href": "http://jsonapi.org/format/1.0/"
                }
            }
        }
    },
    "data": {
        "type": "node--album",
        "id": "df825a90-2fb8-49a1-9fda-022504bc9fb8",
        "links": {
            "self": {
                "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8?resourceVersion=id%3A7"
            }
        },
        "attributes": {
            "drupal_internal__nid": 7,
            "drupal_internal__vid": 7,
            "langcode": "en",
            "revision_timestamp": "2020-11-24T21:00:29+00:00",
            "revision_log": null,
            "status": true,
            "title": "Abbey Road",
            "created": "2020-11-24T20:54:58+00:00",
            "changed": "2020-11-24T21:00:29+00:00",
            "promote": true,
            "sticky": false,
            "default_langcode": true,
            "revision_translation_affected": true,
            "path": {
                "alias": null,
                "pid": null,
                "langcode": "en"
            },
            "field_description": "Abbey Road is the eleventh studio album by the English rock band the Beatles",
            "field_release_date": "1969-09-26T21:49:44+00:00"
        },
        "relationships": {
            "node_type": {
                "data": {
                    "type": "node_type--node_type",
                    "id": "2ba6452d-ac91-48b7-b1a8-a7ffd1ab9cec"
                },
                "links": {
                    "related": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/node_type?resourceVersion=id%3A7"
                    },
                    "self": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/relationships/node_type?resourceVersion=id%3A7"
                    }
                }
            },
            "revision_uid": {
                "data": {
                    "type": "user--user",
                    "id": "441881cb-202d-4804-9832-9f42e32e48c0"
                },
                "links": {
                    "related": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/revision_uid?resourceVersion=id%3A7"
                    },
                    "self": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/relationships/revision_uid?resourceVersion=id%3A7"
                    }
                }
            },
            "uid": {
                "data": {
                    "type": "user--user",
                    "id": "441881cb-202d-4804-9832-9f42e32e48c0"
                },
                "links": {
                    "related": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/uid?resourceVersion=id%3A7"
                    },
                    "self": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/relationships/uid?resourceVersion=id%3A7"
                    }
                }
            },
            "field_artist": {
                "data": {
                    "type": "node--artist",
                    "id": "1e3b51ea-b6b8-417b-89a0-ca14772eb223"
                },
                "links": {
                    "related": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/field_artist?resourceVersion=id%3A7"
                    },
                    "self": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/relationships/field_artist?resourceVersion=id%3A7"
                    }
                }
            },
            "field_cover_art": {
                "data": {
                    "type": "media--image",
                    "id": "f8d292a1-4557-47b0-8f0b-a059f103be24"
                },
                "links": {
                    "related": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/field_cover_art?resourceVersion=id%3A7"
                    },
                    "self": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/relationships/field_cover_art?resourceVersion=id%3A7"
                    }
                }
            },
            "field_tracks": {
                "data": [
                    {
                        "type": "node--track",
                        "id": "922b838c-b63f-42ef-acbb-4e103cf640ac"
                    },
                    {
                        "type": "node--track",
                        "id": "8f15d5d4-1163-4f7b-be98-1b27cf054546"
                    },
                    {
                        "type": "node--track",
                        "id": "6f69fe91-19a7-4a12-92d2-2f0139102072"
                    },
                    {
                        "type": "node--track",
                        "id": "dcc7e5d0-9993-4c57-8a0e-edc9980d6e09"
                    },
                    {
                        "type": "node--track",
                        "id": "923c4da6-b377-4a30-8b81-c980d34f3c92"
                    }
                ],
                "links": {
                    "related": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/field_tracks?resourceVersion=id%3A7"
                    },
                    "self": {
                        "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8/relationships/field_tracks?resourceVersion=id%3A7"
                    }
                }
            }
        }
    },
    "links": {
        "self": {
            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/album/df825a90-2fb8-49a1-9fda-022504bc9fb8"
        }
    }
}

All of the relevant data is easily accessible, though Drupal’s is a little more verbose. This is partly because Drupal’s content entities have more built-in fields by default.

Both list references with their type and their unique ID so that information can be retrieved with subsequent calls.

Contentful:

"sys": {
    "type": "Link",
    "linkType": "Entry",
    "id": "26HTJnrAXT3UgQzPuQx5AA"
}

Drupal:

{
    "type": "node--track",
    "id": "dcc7e5d0-9993-4c57-8a0e-edc9980d6e09"
},

Notice that Contentful’s response has no author or user. This is because its users are purely editorial in context and are not supposed to be part of the model. If you wanted to track authors and pass them via the API, you would need to create an Author content type and another reference field.

Both Contentful and Drupal also allow you to:

  • Resolve the referenced entities' data, so you don’t need to make subsequent API calls for each referenced entity. In our example of an album, we would get terrible performance if we made an API call for every single track. We can add an “include” parameter that puts this information in a separate array.
  • Limit the fields that are returned in a response to limit the verbosity and payload size.

Content Collections

You can pull a collection of entities that match certain criteria, and both have robust filtering mechanisms to find specific content. There are differences in implementation, but you can achieve the same result with both, filtering by any field with a wide range of operators (equal, not equal, greater than, etc.) and ordering the results based on a certain field.

Read about Contentful’s full range of search parameters here and compare them to Drupal’s JSON API filtering capabilities here.

Contentful does include some intriguing full-text search capabilities out-of-the-box that searches across all fields. Typically, a decoupled application will use a dedicated service for search, but if your search needs are rudimentary, Contentful’s default might meet your needs.

On the other hand, because of the way Drupal implements it’s filtering, you can do per-field text searches with pattern operators like 'STARTS_WITH,' 'CONTAINS,' and 'ENDS_WITH.’ This can be useful. 

You can also get a rudimentary full-text search running on Drupal with the JSON: API Search API module.

Here is a Contentful collection example, truncated to the first two results.

Request format: 

https://cdn.contentful.com/spaces/{space_id}/environments/master/entries?access_token={access_token}&content_type={type}


{
    "sys": {
        "type": "Array"
    },
    "total": 9,
    "skip": 0,
    "limit": 100,
    "items": [
        {
            "sys": {
                "space": {
                    "sys": {
                        "type": "Link",
                        "linkType": "Space",
                        "id": "79u36k13bxm5"
                    }
                },
                "id": "7w2rXd9fQfTOXuAEPcqmqB",
                "type": "Entry",
                "createdAt": "2020-11-17T19:38:08.569Z",
                "updatedAt": "2020-11-17T19:38:08.569Z",
                "environment": {
                    "sys": {
                        "id": "master",
                        "type": "Link",
                        "linkType": "Environment"
                    }
                },
                "revision": 1,
                "contentType": {
                    "sys": {
                        "type": "Link",
                        "linkType": "ContentType",
                        "id": "song"
                    }
                },
                "locale": "en-US"
            },
            "fields": {
                "title": "Please Please Me"
            }
        },
        {
            "sys": {
                "space": {
                    "sys": {
                        "type": "Link",
                        "linkType": "Space",
                        "id": "79u36k13bxm5"
                    }
                },
                "id": "7uFcbd7Q3vyG3kpibaPWfp",
                "type": "Entry",
                "createdAt": "2020-11-17T19:37:58.370Z",
                "updatedAt": "2020-11-17T19:37:58.370Z",
                "environment": {
                    "sys": {
                        "id": "master",
                        "type": "Link",
                        "linkType": "Environment"
                    }
                },
                "revision": 1,
                "contentType": {
                    "sys": {
                        "type": "Link",
                        "linkType": "ContentType",
                        "id": "song"
                    }
                },
                "locale": "en-US"
            },
            "fields": {
                "title": "Ask Me Why"
            }
        },
        ..
        .
    ]
}

And here is the Drupal JSON API example, also truncated to the first two results. 

Request format: https://example.com/jsonapi/node/{type}


{
    "jsonapi": {
        "version": "1.0",
        "meta": {
            "links": {
                "self": {
                    "href": "http://jsonapi.org/format/1.0/"
                }
            }
        }
    },
    "data": [
        {
            "type": "node--track",
            "id": "922b838c-b63f-42ef-acbb-4e103cf640ac",
            "links": {
                "self": {
                    "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/922b838c-b63f-42ef-acbb-4e103cf640ac?resourceVersion=id%3A2"
                }
            },
            "attributes": {
                "drupal_internal__nid": 2,
                "drupal_internal__vid": 2,
                "langcode": "en",
                "revision_timestamp": "2020-11-24T20:58:56+00:00",
                "revision_log": null,
                "status": true,
                "title": "Come Together",
                "created": "2020-11-24T20:58:56+00:00",
                "changed": "2020-11-24T20:58:56+00:00",
                "promote": true,
                "sticky": false,
                "default_langcode": true,
                "revision_translation_affected": true,
                "path": {
                    "alias": null,
                    "pid": null,
                    "langcode": "en"
                }
            },
            "relationships": {
                "node_type": {
                    "data": {
                        "type": "node_type--node_type",
                        "id": "d773d9e2-9c75-4547-98af-47cc41f5b28e"
                    },
                    "links": {
                        "related": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/922b838c-b63f-42ef-acbb-4e103cf640ac/node_type?resourceVersion=id%3A2"
                        },
                        "self": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/922b838c-b63f-42ef-acbb-4e103cf640ac/relationships/node_type?resourceVersion=id%3A2"
                        }
                    }
                },
                "revision_uid": {
                    "data": {
                        "type": "user--user",
                        "id": "441881cb-202d-4804-9832-9f42e32e48c0"
                    },
                    "links": {
                        "related": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/922b838c-b63f-42ef-acbb-4e103cf640ac/revision_uid?resourceVersion=id%3A2"
                        },
                        "self": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/922b838c-b63f-42ef-acbb-4e103cf640ac/relationships/revision_uid?resourceVersion=id%3A2"
                        }
                    }
                },
                "uid": {
                    "data": {
                        "type": "user--user",
                        "id": "441881cb-202d-4804-9832-9f42e32e48c0"
                    },
                    "links": {
                        "related": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/922b838c-b63f-42ef-acbb-4e103cf640ac/uid?resourceVersion=id%3A2"
                        },
                        "self": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/922b838c-b63f-42ef-acbb-4e103cf640ac/relationships/uid?resourceVersion=id%3A2"
                        }
                    }
                }
            }
        },
        {
            "type": "node--track",
            "id": "8f15d5d4-1163-4f7b-be98-1b27cf054546",
            "links": {
                "self": {
                    "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/8f15d5d4-1163-4f7b-be98-1b27cf054546?resourceVersion=id%3A3"
                }
            },
            "attributes": {
                "drupal_internal__nid": 3,
                "drupal_internal__vid": 3,
                "langcode": "en",
                "revision_timestamp": "2020-11-24T20:59:01+00:00",
                "revision_log": null,
                "status": true,
                "title": "Something",
                "created": "2020-11-24T20:59:01+00:00",
                "changed": "2020-11-24T20:59:01+00:00",
                "promote": true,
                "sticky": false,
                "default_langcode": true,
                "revision_translation_affected": true,
                "path": {
                    "alias": null,
                    "pid": null,
                    "langcode": "en"
                }
            },
            "relationships": {
                "node_type": {
                    "data": {
                        "type": "node_type--node_type",
                        "id": "d773d9e2-9c75-4547-98af-47cc41f5b28e"
                    },
                    "links": {
                        "related": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/8f15d5d4-1163-4f7b-be98-1b27cf054546/node_type?resourceVersion=id%3A3"
                        },
                        "self": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/8f15d5d4-1163-4f7b-be98-1b27cf054546/relationships/node_type?resourceVersion=id%3A3"
                        }
                    }
                },
                "revision_uid": {
                    "data": {
                        "type": "user--user",
                        "id": "441881cb-202d-4804-9832-9f42e32e48c0"
                    },
                    "links": {
                        "related": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/8f15d5d4-1163-4f7b-be98-1b27cf054546/revision_uid?resourceVersion=id%3A3"
                        },
                        "self": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/8f15d5d4-1163-4f7b-be98-1b27cf054546/relationships/revision_uid?resourceVersion=id%3A3"
                        }
                    }
                },
                "uid": {
                    "data": {
                        "type": "user--user",
                        "id": "441881cb-202d-4804-9832-9f42e32e48c0"
                    },
                    "links": {
                        "related": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/8f15d5d4-1163-4f7b-be98-1b27cf054546/uid?resourceVersion=id%3A3"
                        },
                        "self": {
                            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track/8f15d5d4-1163-4f7b-be98-1b27cf054546/relationships/uid?resourceVersion=id%3A3"
                        }
                    }
                }
            }
        },
        ..
        .
    ],
    "links": {
        "self": {
            "href": "https://dev-music-library-demo.pantheonsite.io/jsonapi/node/track"
        }
    }
}

Rich Text and Embeds

Each system handles rich text and embeds in different ways. First, let’s look at how Contentful returns content with embedded assets.

For this example, we’ll pull our Artist content titled “The Beatles.” This is how the About field is represented:

"about": {
            "data": {},
            "content": [
                {
                    "data": {},
                    "content": [
                        {
                            "data": {},
                            "marks": [],
                            "value": "The Beatles were an English rock band formed in Liverpool in 1960. The group, whose best-known line-up comprised John Lennon, Paul McCartney, George Harrison and Ringo Starr, are regarded as the most influential band of all time.",
                            "nodeType": "text"
                        }
                    ],
                    "nodeType": "paragraph"
                },
                {
                    "data": {},
                    "content": [
                        {
                            "data": {},
                            "marks": [],
                            "value": "They were integral to the development of 1960s counterculture and popular music's recognition as an art form.Rooted in skiffle, beat and 1950s rock and roll, their sound incorporated elements of classical music and traditional pop in innovative ways; the band later explored music styles ranging from ballads and Indian music to psychedelia and hard rock. ",
                            "nodeType": "text"
                        }
                    ],
                    "nodeType": "paragraph"
                },
                {
                    "data": {
                        "target": {
                            "sys": {
                                "id": "6NwAnNO3L14TjyfCpvrf0l",
                                "type": "Link",
                                "linkType": "Asset"
                            }
                        }
                    },
                    "content": [],
                    "nodeType": "embedded-asset-block"
                },
                {
                    "data": {},
                    "content": [
                        {
                            "data": {},
                            "marks": [],
                            "value": "As pioneers in recording, songwriting and artistic presentation, the Beatles revolutionised many aspects of the music industry and were often publicised as leaders of the era's youth and sociocultural movements.",
                            "nodeType": "text"
                        }
                    ],
                    "nodeType": "paragraph"
                },
                {
                    "data": {
                        "target": {
                            "sys": {
                                "id": "XLrFYAYIhAMh7thYq2gxs",
                                "type": "Link",
                                "linkType": "Entry"
                            }
                        }
                    },
                    "content": [],
                    "nodeType": "embedded-entry-block"
                },
                {
                    "data": {},
                    "content": [
                        {
                            "data": {},
                            "marks": [],
                            "value": "Nationwide interest in the Beatles had been piqued with the success of their second UK single and ",
                            "nodeType": "text"
                        },
                        {
                            "data": {
                                "uri": "https://en.wikipedia.org/wiki/Parlophone"
                            },
                            "content": [
                                {
                                    "data": {},
                                    "marks": [],
                                    "value": "Parlophone",
                                    "nodeType": "text"
                                }
                            ],
                            "nodeType": "hyperlink"
                        },
                        {
                            "data": {},
                            "marks": [],
                            "value": ", hoping to take advantage of this, promptly decided to follow it up with an album.",
                            "nodeType": "text"
                        }
                    ],
                    "nodeType": "paragraph"
                },
                {
                    "data": {},
                    "content": [
                        {
                            "data": {},
                            "marks": [],
                            "value": "",
                            "nodeType": "text"
                        }
                    ],
                    "nodeType": "paragraph"
                },
                {
                    "data": {},
                    "content": [
                        {
                            "data": {},
                            "marks": [],
                            "value": "",
                            "nodeType": "text"
                        }
                    ],
                    "nodeType": "paragraph"
                }
            ],
            "nodeType": "document"
        },

It contains no HTML markup. Each paragraph is its own “node” with a nodeType of “paragraph,” each with its own content array of “nodes.” Even links are represented as a different “node.”

Embedded content like images is represented as “embedded-asset-block” or “embedded-entry-block,” with all the data you need to get the information for those items.

If the query is built right, you can also include all of the data for embedded entities in the same API call.

This means you need to build up a page yourself on the front-end. This is like a LEGO brick set that comes with clear instructions. It’s very flexible and makes no assumptions about where this data is going to be rendered.

To compare, here is how Drupal handles a call for the same content.

"field_about": {
        "value": "<p>The Beatles were an English rock band formed in Liverpool in 1960. The group, whose best-known line-up comprised John Lennon, Paul McCartney, George Harrison and Ringo Starr, are regarded as the most influential band of all time.</p>\r\n\r\n<p>They were integral to the development of 1960s counterculture and popular music's recognition as an art form.Rooted in skiffle, beat and 1950s rock and roll, their sound incorporated elements of classical music and traditional pop in innovative ways; the band later explored music styles ranging from ballads and Indian music to psychedelia and hard rock.</p>\r\n\r\n<drupal-media data-align=\"center\" data-entity-type=\"media\" data-entity-uuid=\"5ce77cc7-3a20-4fee-b6d7-e2ad1f611d40\"></drupal-media>\r\n\r\n<p>As pioneers in recording, songwriting and artistic presentation, the Beatles revolutionised many aspects of the music industry and were often publicised as leaders of the era's youth and sociocultural movements.</p>\r\n\r\n<drupal-entity data-embed-button=\"node\" data-entity-embed-display=\"view_mode:node.full\" data-entity-type=\"node\" data-entity-uuid=\"44ffdcc0-568d-41c5-ba92-b2a04a26eb8f\" data-langcode=\"en\"></drupal-entity>\r\n\r\n<p>&nbsp;</p>\r\n\r\n<p>Nationwide interest in the Beatles had been piqued with the success of their second UK single and&nbsp;<a href=\"https://en.wikipedia.org/wiki/Parlophone\">Parlophone</a>, hoping to take advantage of this, promptly decided to follow it up with an album.</p>\r\n",
        "format": "basic_html",
        "processed": "<p>The Beatles were an English rock band formed in Liverpool in 1960. The group, whose best-known line-up comprised John Lennon, Paul McCartney, George Harrison and Ringo Starr, are regarded as the most influential band of all time.</p>\n\n<p>They were integral to the development of 1960s counterculture and popular music's recognition as an art form.Rooted in skiffle, beat and 1950s rock and roll, their sound incorporated elements of classical music and traditional pop in innovative ways; the band later explored music styles ranging from ballads and Indian music to psychedelia and hard rock.</p>..."
},

This is a different approach. Drupal includes both the unfiltered markup and the processed markup for the field, based on the selected format. For simple content, you can just take what is returned in the “processed” key and render it. If you set things up on the back-end properly, this even works with embeds.

For some sites, that’s all you need.

This is the isolated embed code for a media asset and a content entity:

<drupal-media data-align=\"center\" data-entity-type=\"media\" data-entity-uuid=\"5ce77cc7-3a20-4fee-b6d7-e2ad1f611d40\"></drupal-media>

<drupal-entity data-embed-button=\"node\" data-entity-embed-display=\"view_mode:node.full\" data-entity-type=\"node\" data-entity-uuid=\"44ffdcc0-568d-41c5-ba92-b2a04a26eb8f\" data-langcode=\"en\"></drupal-entity>

If you want to handle rendering in a more ad-hoc manner, with more flexibility, you will need some way to save embedded entities into an entity reference field on the node. This will let you include their data in the same API request. 

It requires some additional processing on the back-end, as you need to parse the rich text field and grab the entities embedded there. We did something similar on Edutopia with great success.

Conclusion

We could compare a lot more, like previews and image derivatives, but this gives you a good introduction to the differences.

Bottom line: anything Contentful does, Drupal can do as well. You can treat Drupal as a hub for your content, and it will perform admirably. Most gaps in Drupal Core have been filled with stable, secure, and reliable modules.

Contentful is a true API-first platform. It was designed with front-end consumers in mind, and it is very robust in that regard.

Drupal’s focus has been on managing large amounts of content and the editorial workflows that can come with that, as well as modeling diverse types of content. Its API capabilities are good and stable, but they have not been its sole focus. As a result, sometimes there are additional hoops you need to jump through to get your desired result.

Back-end customizations can be done for both Contentful and Drupal. For Contentful, you will want deep React experience. With Drupal, deep PHP experience. 

What does your team look like? If you are hiring an agency to help, what is their expertise? These should be considered while weighing the decision.

Since Contentful is a SaaS product, it has a very polished feature set. But this comes with limited extensibility. Your business will need to adapt to Contentful’s capabilities. Smaller companies will have no problem with this, but larger companies, or companies that are growing fast, might reach the bars of the cage sooner than they thought, finding themselves unable to flex or maneuver.

Drupal, by contrast, cannot be as polished since it doesn’t require you to go down a limited number of predetermined paths. As a result, Drupal places no arbitrary roadblocks to implementing what you need.

If you have a large editorial team with complicated workflow requirements, Drupal is ready to change for your needs. To even start with custom roles on Contentful, you will need the Enterprise tier.

In fact, for most companies with a large web presence, if you go with Contentful, you will need to go with their Enterprise tier. There are too many limitations with the Team tier. This means long-term contracts and big licensing fees. You get a lot in return, like uptime and support SLAs, but you need to take that into consideration.

What are the key advantages of Drupal compared to Contentful?

  • Unlimited users, roles, and permissions with unlimited workflow possibilities.
  • Full translatable content with unlimited locales, including built-in translations for the editorial interface.
  • Large contrib module ecosystem, providing more ways to customize the editorial experience.
  • Granular customizations of API responses.
  • Mature content modeling with unlimited content types.
  • Open-source with no licensing fees and no long-term contracts.

Do you have a decoupled project you are considering? We have helped solve some of the hardest decoupled problems with Drupal, and we would love to help you. Our experience is concrete and practical, not theoretical.

Contact us today.

Matt Robison

Thumbnail
Matt has been working with Drupal since 2008. He loves spending his time reading, writing, playing with his three kids, and eating lots of ice cream.