Szymon Uryga
May 19, 2025
visibility 2586
star star star star star star
(5 votes)

Common Mistakes in Headless Projects with Optimizely

Adopting a headless architecture with Optimizely is a major shift from the traditional MVC-based development that has been the standard for years. While the flexibility and performance of headless setups are often worth the transition, many teams make avoidable mistakes during their first headless implementations. In this post, I’ll highlight some of the most common issues I’ve seen - and how to avoid them.

 

1. Overemphasis on PaaS vs. SaaS

One of the first decisions teams face is whether to use Optimizely's PaaS (Platform as a Service) or SaaS (Software as a Service) version. Unfortunately, many technical teams get stuck in the weeds of this decision.

The reality? From a headless application perspective, both versions ultimately store and serve content via Optimizely Graph. That’s what your frontend connects to. So the technical differences are minimal in the context of a headless frontend.

What matters more is whether the SaaS version meets all your business and integration requirements. If you need heavy customizations, advanced workflows, or .NET-based extensions, then PaaS might be necessary. Otherwise, SaaS can offer faster onboarding, better scalability, and less maintenance.

 

2. Ignoring Auto-Generated Types from Optimizely Graph

Optimizely Graph supports code generation (codegen), which allows developers to generate strongly typed classes based on your content model.

Yet, in many projects, teams skip this and manually type out GraphQL queries and expect responses as raw JSON. This leads to:

  • Guesswork about what fields are available

  • Increased chance of typos

  • Fragile code due to schema mismatches

  • Slower development and debugging

Recommendation: Use the codegen tools provided by Optimizely Graph to automatically generate TypeScript (or your preferred language) types. This gives developers instant access to the schema, autocomplete, and type checking — significantly reducing runtime bugs and increasing productivity.

 

3. Header and Footer Misplaced as Homepage Blocks

A common anti-pattern in headless Optimizely projects is placing global components like the header and footer directly on the homepage as blocks. While it may seem convenient, it creates serious architectural problems.

Why it’s a problem:

  • Webhooks only detect changes to the homepage, so header/footer changes won’t trigger cache invalidation unless the entire homepage is republished.

  • The frontend is forced to rebuild or refetch the entire homepage, even if only the footer changes.

  • It creates a tight coupling between unrelated content.

Recommendation: Treat the header and footer as independent content items in the CMS - for example, create two separate pages: SiteHeader and SiteFooter. This allows fine-grained cache invalidation, better content reuse, and simpler frontend logic.

4. Exposing Internal CMS URLs

Another common mistake is exposing internal CMS URLs (e.g., for images or links) directly in frontend code. This happens when frontend apps render URLs exactly as returned by Optimizely - which often point to internal environments or CMS instances.

This is both a security issue and a performance problem:

  • URLs may break if environments change

  • Internal infrastructure is exposed to the public

Recommendation: Always serve media and links via a frontend-friendly abstraction layer. Ideally, images and files should be proxied through a CDN or asset management service. Any CMS-specific URLs should be sanitized or mapped before being passed to the client.

 

Final Thoughts

Headless CMS development offers flexibility, scalability, and performance - but it comes with new patterns and pitfalls. Many of the issues listed here stem from applying traditional CMS thinking to a modern headless setup.

If you're starting a headless project with Optimizely, take time to:

  • Think in terms of APIs, not pages.

  • Embrace tooling like GraphQL codegen.

  • Decouple global content from page-specific content.

  • Abstract all internal CMS data behind your frontend.

By avoiding these common mistakes, you’ll not only improve the developer experience but also deliver faster, more maintainable digital experiences.

 

May 19, 2025

Comments

Kashif Jawed
Kashif Jawed May 20, 2025 07:22 AM

Hi @szymon


Nice article for the common mistakes.

We are currently developing a solution using a content graph architecture with an Angular micro-frontend application.

Regarding point #4 — Exposing Internal CMS URLs — we’re encountering a similar issue. Could you please provide more details or clarify the recommended approach to address this?

Szymon Uryga
Szymon Uryga May 23, 2025 09:42 AM

Hi! Thanks for your message - glad to hear the article was helpful.

Regarding point #4 - Exposing Internal CMS URLs, the best practice here is:

  1. Use relative URLs in the data returned from the Content Graph - this prevents exposing full internal CMS URLs to the frontend.

  2. In your GraphQL queries, simply avoid requesting fields that contain full URLs unless absolutely necessary.

  3. Additionally, it’s important to ensure that all communication with the Content Graph happens server-to-server. This approach offers several advantages:
    • You avoid exposing CMS endpoints directly to the client,

    • You reduce the risk of GraphQL query manipulation from the browser.

error Please login to comment.
Latest blogs
Composition Over Inheritance: Why Your Content Contract Is Not a Base Class

Inside the structural conformance pattern in SaaS CMS — from server-side inheritance to pipeline governance. A contract in Optimizely SaaS CMS is...

Vipin Banka | Sep 19, 2026

Optimizely Opti ID Integration: Why the API Key Was Missing from the DXP Management Portal

While working on an Optimizely CMS 12 Opti ID integration recently, we ran into an issue that initially appeared to be related to SSO authenticatio...

Madhu | Sep 18, 2026 |

Extending a Contract: What You Can Override

Contracts share fields across content types — but not every field setting can be overridden. Push the same contract onto several components, and CM...

Chirag Khanna | Sep 18, 2026 |

Building a Blazor Server UI Inside the Optimizely CMS 13 Admin Shell

What I learned shipping a Blazor Server UI inside Optimizely CMS 13 — Razor components in a NuGet package, the Blazor hub next to MapContent(),...

Stanisław Szołkowski | Sep 17, 2026 |