Skip to content
Talk to our solutions team

Content

The Content block stores and serves content: the bytes of your media, and the structured entries that describe them. The deployable service is content.svc.

It is headless in the strict sense. The block owns modelling, storage, localisation, publication and delivery. Your framework owns rendering and routing, which is why an Astro, Next or Nuxt site can sit in front of it unchanged.

One instance answers the Strapi REST API and the Contentful Delivery API at the same time, over the same content and the same store.

GET /api/:plural Strapi delivery
GET /api/:plural/:documentId
POST /api/:plural Strapi write
GET /spaces/:space/environments/:env/entries Contentful (CDA)
GET /spaces/:space/environments/:env/entries/:id
GET /spaces/:space/environments/:env/content_types

These are each vendor’s own paths, unprefixed. A site already written against either one needs its base URL changed and nothing else.

The two surfaces share an identity: Contentful’s sys.id and Strapi’s documentId are the same key. An entry that one client resolved through a link is findable by the other under the same name.

There is no second modelling system and no registry of type definitions. A content type is a Data entity, and an entry is a row. A cms: block on the entity is what makes it a content type:

entities:
- name: blog.post
cms:
kind: entry # entry | singleton
space: blog
access:
actions:
read: { language: expr, expression: 'true' }
update: { language: expr, expression: '"editor" in user.roles' }
fields:
- name: title
type: string
required: true
cms: { localized: true }
- name: hero
type: file # a reference into the asset plane
nullable: true
- name: price
type: int
cms: { localized: false }

Four columns carry the CMS semantics and are added for you at engine build: documentid, locale, status and publishedon. You declare the fields you care about.

See The content model.

A space is a content silo inside a tenant, with its own types, its own content and its own delivery keys. It maps to a Contentful space and to a Strapi project.

The entity name carries the space, so blog.post and docs.post are two different types that may share a name and differ in shape. A space is a naming dimension inside the tenant’s namespace, not a separate database schema, so adding one costs nothing at the isolation layer.

The content plane and the asset plane have different shapes and are kept apart.

Content planeAsset plane
Unitan entry (a row)a file and its metadata
Addressed bya querya path or a ref
Hot pathan indexed readstreamed bytes
Source of truththe databasethe metadata sidecar in the store
Authorizationentity access rulesthe store’s path and prefix policy

They meet at exactly one seam: a field of type file holds an opaque reference to an asset, and the delivery path resolves it to a URL. See Assets.

Content authorization is the Data block’s entity access and nothing else. There is one tier and one vocabulary, so a rule means the same thing on a delivery read, a write and a draft preview.

access:
actions:
read: { language: expr, expression: 'true' }
create: { language: expr, expression: '"editor" in user.roles' }
update: { language: expr, expression: '"editor" in user.roles' }
delete: { language: expr, expression: '"admin" in user.roles' }

Everything on Access rules, Row-level security and Field compliance directives applies to content unchanged.

Unpublished content is gated on permission to edit the type. You may preview what you may change. A front end that holds only a delivery credential renders drafts through a preview token, which is a short-lived, signed, scoped grant.

ConcernPage
One content type, end to endQuickstart
cms: blocks, spaces, locales, relations, publicationThe content model
/api/:plural, filters, populate, pagination, writesStrapi delivery
sys envelopes, includes, the locale pivotContentful delivery
Rendering drafts from a front endPreview
Stores, uploads, ranged reads, presigned redirectsAssets
?w=800&fm=webp, named formats, the native grammarImage transforms
Every route, parameter and statusAPI
Configuration, storage modes, webhooks, what to watchOperations