contentful
The Contentful CMS for Evolve: a module that serves the shared content contract (pages, content blocks, menus and the site) from a Contentful space, so a storefront reads content the same way whichever CMS is behind it.
When to use
Use it when Contentful is your CMS. It runs next to a commerce engine, often
in the same service as the catalog, and its webhook publishes
content-modified events when editors publish. Add your own content types as
blocks next to the built-in ones. Use @evolve-framework/storyblok or
@evolve-framework/payload instead for those CMSes.
Get started
Install the package with the peer dependencies it needs. It comes from the Evolve registry, which needs a one-time sign-in.
pnpm add @evolve-framework/contentful @evolve-framework/core fastify graphql graphql-yoga zodRequirements7
@evolve-framework/core>= 1.0.0 < 2.0.0@types/node>=22optionalfastify>= 5.12.1 < 6.0.0graphql>= 16.0.0 < 17.0.0graphql-yoga>= 5.21.3 < 6.0.0msw^2.14.6 || ^3.0.1optionalzod>= 4.4.0 < 5.0.0
For example:
import { ContentfulModule } from "@evolve-framework/contentful";import { NoopPublisher } from "@evolve-framework/core/messaging";
const cms = new ContentfulModule({ config: { spaceId: process.env.CONTENTFUL_SPACE_ID!, environment: process.env.CONTENTFUL_ENVIRONMENT ?? "master", accessToken: process.env.CONTENTFUL_ACCESS_TOKEN!, previewToken: process.env.CONTENTFUL_PREVIEW_TOKEN!, managementToken: process.env.CONTENTFUL_MANAGEMENT_API_ACCESS_TOKEN!, }, // Where content-modified events go; a cloud adapter's publisher in production messagePublisher: new NoopPublisher(), // The signing secret of the space's webhook request verification webhookSecret: process.env.CONTENTFUL_WEBHOOK_SECRET!,});Modules
ContentfulModule
Module wiring Contentful resolvers, typedefs and webhook into a Domain Service.
Pass either a pre-built clients bundle (from createContentfulClients)
or a config object; the module will build its own clients from config
during init().
Only one ContentfulModule instance per process is supported โ clients and the mapper registry are held as module-scope singletons.
import { ContentfulModule } from "@evolve-framework/contentful";new ContentfulModule(options: ContentfulModuleOptions)| Option | Type | Description |
|---|---|---|
messagePublisher | MessagePublisher | Publisher used to emit |
webhookSecret | string | The signing secret of the space's webhook request verification, under Settings > Webhooks > Settings in Contentful. A delivery without a valid signature is refused with a 401. |
webhookPath? | string | URL path the webhook is registered on. Defaults to "/webhook". |
blocks? | ContentBlockDef[] | The complete set of content blocks the module serves. Defaults to the
built-in |
mappers? | ((registry: MapperRegistry) => void) | Optional callback invoked with the pre-populated MapperRegistry. Use it to add or override mappers for custom Contentful content types. |
articlesPerProduct? | number | How many articles a product shows at most. Defaults to 4. |
siteRevalidation? | SiteRevalidationOptions | The sites whose caches a publish purges: their revalidate URLs and the secret they share with this service. The locales purged are the space's. Without it the webhook purges only this service's caches. |
clients? | ContentfulClients | Clients the module uses as they are, typically from
|
config? | ContentfulConfig | Settings the module builds its own clients from with
|
ContentBlockDef8The fields of blocks.
| Field | Type | Description |
|---|---|---|
id | string | Contentful |
typename | string | Output GraphQL typename, e.g. "PromoList". |
typeDefs? | string | SDL for a block the shared contract does not already describe. Built-in
blocks leave this unset: their output type and union membership are part
of the contract every CMS package serves, so they live in |
terraform? | string | Terraform provisioning the content type in Contentful. |
groups? | string[] | Groups this block is a member of (which slots it can fill). |
slots? | Record<string, Slot> | Slots this block exposes (what it can contain). |
mapper? | ContentBlockMapper | Maps a Contentful entry to the block's GraphQL type, for blocks dispatched
through the mapper registry (body/column blocks). Omitted for blocks whose
mapping lives elsewhere โ e.g. pages (see |
pageMapper? | PageMapper | Maps an entry of this page type onto its GraphQL page. Set only on page
types; |
ContentfulClients5The fields of clients.
| Field | Type | Description |
|---|---|---|
client | ContentfulClientApi<undefined> | |
previewClient | ContentfulClientApi<undefined> | |
managementClient | PlainClientAPI | |
spaceId | string | |
environment | string |
ContentfulConfig5The fields of config.
| Field | Type | Description |
|---|---|---|
spaceId | string | |
environment | string | |
accessToken | string | |
previewToken | string | |
managementToken | string |
Extends AbstractModule<ContentfulLoaderContext>.
Testing
Test helpers in /testing. What they are for
Commands
Installing this package adds these commands to the evolve CLI.
evolve eject: Eject built-in blocks into your project so you own themevolve field-tabs: Put every content type's fields on the Content, SEO and Settings tabsevolve tf-export: Export the built-in content blocks' Terraform to disk
Entry points
@evolve-framework/contentful | ContentfulModule, the built-in content blocks and defineContentBlock for your own, the Contentful clients and configuration, and catalog page sync.52 exports |
@evolve-framework/contentful/graphql/context | The request context the module's resolvers expect.3 exports |
@evolve-framework/contentful/graphql/types | The generated GraphQL types of the content contract.236 exports |
@evolve-framework/contentful/cftypes | Generated TypeScript types of the Contentful content types the module provisions.113 exports |
@evolve-framework/contentful/testing | A test context, a mocked Contentful API and a test executor.4 exports |

