Schema Generation and Customization
Why schema matters
Section titled “Why schema matters”Every GraphQL API is defined by its schema — the contract between server and client that describes what data is available and how to query it. In Optimizely Graph, this schema is generated automatically from your CMS content model. Understanding how this generation works helps you design content types that produce clean, predictable APIs for frontend teams.
Automatic schema generation
Section titled “Automatic schema generation”From content type to GraphQL type
Section titled “From content type to GraphQL type”Each content type you define in the CMS becomes a corresponding GraphQL type in Graph. Properties on that content type become fields on the GraphQL type, with CMS property types mapped to GraphQL scalar types.
[ContentType(
DisplayName = "Article Page",
GUID = "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
Description = "A page for news articles"
)]
public class ArticlePage : PageData
{
[Display(Name = "Headline", Order = 10)]
[Required]
public virtual string Headline { get; set; }
[Display(Name = "Author Name", Order = 20)]
public virtual string Author { get; set; }
[Display(Name = "Publish Date", Order = 30)]
public virtual DateTime PublishDate { get; set; }
[Display(Name = "Article Body", Order = 40)]
public virtual XhtmlString ArticleBody { get; set; }
[Display(Name = "Tags", Order = 50)]
public virtual IList<string> Tags { get; set; }
} type ArticlePage {
Headline: String!
Author: String
PublishDate: DateTime
ArticleBody: RichText
Tags: [String]
_metadata: ContentMetadata
_fulltext: [String]
_score: Float
} Type mapping rules
Section titled “Type mapping rules”Graph follows predictable mapping rules when converting CMS types to GraphQL types:
| CMS Property Type | GraphQL Type | Notes |
|---|---|---|
string | String | Single-line text |
XhtmlString | RichText | Contains html and text subfields |
int | Int | Integer values |
double / decimal | Float | Floating-point numbers |
bool | Boolean | True/false values |
DateTime | DateTime | ISO 8601 formatted |
ContentReference | ContentRef | Link to another content item |
ContentArea | [ContentAreaItem] | Ordered collection of content blocks |
IList<string> | [String] | Array of strings |
SelectOne | String | Selected option value |
SelectMany | [String] | Multiple selected values |
Inheritance and interfaces
Section titled “Inheritance and interfaces”CMS content type inheritance is reflected in the GraphQL schema through interfaces. If ArticlePage and BlogPost both inherit from PageData, Graph generates a shared interface that allows polymorphic queries.
query GetAllPages {
Content(
where: {
_metadata: { types: { eq: "Page" } }
}
limit: 20
) {
items {
_metadata {
key
types
url { default }
}
... on ArticlePage {
Headline
Author
}
... on BlogPost {
Title
BlogAuthor
}
... on ProductPage {
ProductName
Price
}
}
}
} Inline fragments (... on TypeName) let you select type-specific fields while querying a shared interface. This is essential for content areas that can contain mixed content types.
Customizing the schema
Section titled “Customizing the schema”Property indexing control
Section titled “Property indexing control”Not every property needs to appear in Graph. You can control which properties are indexed using attributes:
public class ArticlePage : PageData
{
// Indexed and searchable (default behavior)
public virtual string Headline { get; set; }
// Indexed but excluded from full-text search
[GraphIgnoreFullText]
public virtual string InternalNotes { get; set; }
// Completely excluded from Graph
[GraphIgnore]
public virtual string EditorOnlyField { get; set; }
// Searchable with boosted relevance
[GraphSearchable(Boost = 2.0)]
public virtual string Summary { get; set; }
} Custom field extensions
Section titled “Custom field extensions”Graph supports extending the generated schema with computed or transformed fields. These fields do not exist in the CMS but are available in GraphQL queries.
Common use cases:
- Concatenated fields — Combine first name and last name into a full name
- Computed values — Calculate reading time from body text length
- Formatted dates — Provide locale-specific date strings
Schema evolution
Section titled “Schema evolution”Adding new content types
Section titled “Adding new content types”When you create a new content type in the CMS and publish content of that type, Graph automatically detects the new type during the next sync. The GraphQL schema updates to include the new type and its fields.
Modifying existing types
Section titled “Modifying existing types”Changes to content type properties (adding, renaming, or removing fields) require a full resync to update the Graph schema. During the resync, Graph regenerates the schema from the current CMS content model.
Important: Renaming a property creates a new field in Graph and removes the old one. Frontend applications querying the old field name will break. Coordinate schema changes with frontend teams.
Deprecation strategy
Section titled “Deprecation strategy”When removing a field from a content type:
- Add the new field alongside the old one
- Migrate content to use the new field
- Update all frontend queries to use the new field name
- Remove the old field from the content type
- Trigger a full resync
This staged approach prevents breaking frontend applications during schema changes.
Schema exploration
Section titled “Schema exploration”Graph provides introspection capabilities that let you explore the current schema programmatically:
query SchemaExploration {
__type(name: "ArticlePage") {
name
fields {
name
type {
name
kind
}
}
}
} Use the GraphQL playground at https://cg.optimizely.com/content/v2?auth={SingleKey} to explore your schema interactively. The playground provides auto-completion, documentation, and type information as you build queries.
1. A developer renames a CMS property from 'Author' to 'AuthorName' and triggers a resync. Frontend applications that query the 'Author' field start returning errors. What should the team have done differently?
The staged deprecation strategy — add the new field alongside the old, migrate frontends, then remove the old field — prevents breaking changes during schema evolution.
The staged deprecation strategy — add the new field alongside the old, migrate frontends, then remove the old field — prevents breaking changes during schema evolution.
Review this topic →2. A content architect defines a CMS content type with an XhtmlString property called 'ArticleBody'. How will this property appear in the generated GraphQL schema?
XhtmlString CMS properties are mapped to the RichText GraphQL type, which provides both html and text subfields for flexible frontend consumption.
XhtmlString CMS properties are mapped to the RichText GraphQL type, which provides both html and text subfields for flexible frontend consumption.
Review this topic →3. A frontend developer needs to query a content area that can contain ArticlePage, BlogPost, and ProductPage blocks. Which GraphQL feature should they use to select type-specific fields?
Inline fragments (... on TypeName) let you select type-specific fields while querying a shared interface, which is essential for content areas containing mixed content types.
Inline fragments (... on TypeName) let you select type-specific fields while querying a shared interface, which is essential for content areas containing mixed content types.
Review this topic →