Skip to main content
Version: 3.x

Content Types

Content Types empower developers to craft autonomous, reusable schemas. These can be employed by Content Managers to assemble dynamic web pages. This guide elucidates how to utilize the provided tool to create content-driven dynamic pages.

Expose Data in GraphQL

Initially, you must update your GraphQL Schema to mirror the ContentType definitions, which will be structured as a GraphQL union type.

type AcmeHomePage {
title: String
blocks: [AcmeHomePageBlock]
seo: Seo
}

type AcmeSeo {
title: String
description: String
}

union AcmeHomePageBlock = BlockTitleText | BlockTextImage

type BlockTitleText {
title: String
subtitle: String
align: String
}

type BlockTextImage {
title: String
picture: String
picturePosition: String
}

Crafting ContentTypes

To commence, create your initial ContentType for AcmeHomepage, which will be utilized to populate our homepage.

The constructor of the ContentType class accepts four arguments:

  • id - The Content type id as displayed on the Contentful model detail screen.
  • fcGraphQLType - The GraphQL type defined in your schema.
  • dataFormatter - A function transforming Contentful data to fit your schema.
./my-extension/cms/contentType/Homepage.js
import { ContentType } from "@front-commerce/contentful";
import gql from "graphql-tag";

const formatContentfulData = (contentfulData) => {
return {
title: contentfulData.pageTitle,
slug: contentfulData.pageSlug,
};
};

class Homepage extends ContentType {
constructor() {
super("homepage", "AcmeHomepage", formatContentfulData);
}

get contentfulFragment() {
return gql`
fragment HomepageFragment on Homepage {
pageTitle
pageSlug
}
`;
}
}

export default Homepage;

Loading Data with the ContentfulLoader

With the ContentfulLoader, you can implement a contextEnhancer in your module to retrieve homepage data from Contentful.

Should you require a list of content items rather than a singular item, the loadAllContentMatching loader method is at your service. It accepts parameters similar to loadFirstContentMatching, with added pagination arguments:

  • skip - Number of items to bypass.
  • limit - Quantity of items to fetch.
  • order - Ordering criteria, accepted as a string or string[]. For more, see Contentful GraphQL API.

First, create the GraphqlModule file:

./my-extension/cms/graphql.ts
import { createGraphQLModule } from "@front-commerce/core/graphql";

export default createGraphQLModule({
namespace: "Acme/MyModule",
dependencies: ["Contentful"],
loadRuntime: () => import("./runtime"),
});

Then we will create the GraphqlRuntime file:

./my-extension/cms/runtime.ts
import Homepage from "./contentType/Homepage";
import { createGraphQLRuntime } from "@front-commerce/core/graphql";

export default createGraphQLRuntime({
resolvers: {
Query: {
loadHomepages: async (
parent,
args,
{ loaders: { ContentfulHomePage, Contentful } }
) => {
const response = await Contentful.loadAllContentMatching(
ContentfulHomePage,
undefined,
0,
5
);
return response.items.map(HomePage.dataFormatter);
},
},
},

contextEnhancer: ({ req, loaders }) => {
return {
ContentfulHomePage: new Homepage(),
};
},
});

Creating an Independent ContentType

Our present AcmeHomepage is fairly basic. Enhance it by integrating an independent ContentType, such as the seo data for our homepage.

./my-extension/cms/contentType/shared/Seo.js
import { ContentType } from "@front-commerce/contentful";
import gql from "graphql-tag";

const formatContentfulData = (contentfulData) => {
return {
title: contentfulData.seoTitle,
description: contentfulData.seoDescription,
};
};

class Seo extends ContentType {
constructor() {
super("seo", "AcmeSeo", formatContentfulData);
}

get contentfulFragment() {
return gql`
fragment SeoFragment on Seo {
seoTitle
seoDescription
}
`;
}
}

export default Seo;
note

Seo ContentType has its distinct formatter. We transfer it to the homepage formatter and utilize it for formatting the seo data.

Integrating ContentTypes with BlocksContentType

The BlocksContentType is a unique ContentType that facilitates the creation of a repository of ContentTypes. These can be harnessed to generate reusable UI components.

Construct the AcmeHomePageBlock ContentType, which embodies the collection of ContentTypes constituting our union type.

./my-extension/cms/contentType/AcmeHomePageBlock.js
import { BlocksContentType } from "@front-commerce/contentful";

const AcmeHomePageBlock = new BlocksContentType(
"homepage",
"PageContentBlocksItem",
[],
"HomePageBlock"
);

export default AcmeHomePageBlock;

Now, establish the BlockTextTitle ContentType, intended for utilization by the AcmeHomePageBlock ContentType.

./my-extension/cms/contentType/BlockTitleText.js
import { ContentType } from "@front-commerce/contentful";
import gql from "graphql-tag";

const formatContentfulData = (contentfulData) => {
return {
title: contentfulData.blockTitle,
subtitle: contentfulData.excerpt,
};
};

class BlockTextTitle extends ContentType {
constructor() {
super("blockTextTitle", "BlockTextTitle", formatContentfulData);
}

get contentfulFragment() {
return gql`
fragment BlockTextTitleFragment on BlockTextTitle {
blockTitle
excerpt
}
`;
}
}
note

The BlocksContentType has a specialized formatter. We import it to the homepage formatter for blocks data formatting.