Skip to content

Schema Generation and Customization

intermediate
📜CoreGraph

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.

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.

Content type to GraphQL mapping
csharp
[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; }
}
graphql
type ArticlePage {
  Headline: String!
  Author: String
  PublishDate: DateTime
  ArticleBody: RichText
  Tags: [String]
  _metadata: ContentMetadata
  _fulltext: [String]
  _score: Float
}

Graph follows predictable mapping rules when converting CMS types to GraphQL types:

CMS Property TypeGraphQL TypeNotes
stringStringSingle-line text
XhtmlStringRichTextContains html and text subfields
intIntInteger values
double / decimalFloatFloating-point numbers
boolBooleanTrue/false values
DateTimeDateTimeISO 8601 formatted
ContentReferenceContentRefLink to another content item
ContentArea[ContentAreaItem]Ordered collection of content blocks
IList<string>[String]Array of strings
SelectOneStringSelected option value
SelectMany[String]Multiple selected values

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.

Querying across content types with interfaces
graphql
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.

Not every property needs to appear in Graph. You can control which properties are indexed using attributes:

Controlling property indexing
csharp
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; }
}

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

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.

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.

When removing a field from a content type:

  1. Add the new field alongside the old one
  2. Migrate content to use the new field
  3. Update all frontend queries to use the new field name
  4. Remove the old field from the content type
  5. Trigger a full resync

This staged approach prevents breaking frontend applications during schema changes.

Graph provides introspection capabilities that let you explore the current schema programmatically:

Schema introspection query
graphql
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.