Disclaimer: This website requires Please enable JavaScript in your browser settings for the best experience.

Dev guideRecipesAPI ReferenceChangelog
Dev guideRecipesUser GuidesNuGetDev CommunityOptimizely AcademySubmit a ticketLog In
Dev guide

Quick start guide: C#

Connect CMS 12 to Optimizely Graph using C#/.NET.

Build a simple ASP.NET Core application that connects to Optimizely Graph, queries content using GraphQL, and exposes it through a Web API. This guide covers project setup, dependency installation, query definition, GraphQL client configuration, and creating endpoints to retrieve content. Both a manual client and the recommended StrawberryShake approach are included, letting you choose between a lightweight implementation or a strongly-typed, type-safe integration with automatic code generation.

1. Create an ASP.NET Core project

Create a new ASP.NET Core Web API project.

dotnet new webapi -n MyOptimizelyApp
cd MyOptimizelyApp

2. Install dependencies

Install the GraphQL client and configuration extensions.

dotnet add package GraphQL.Client
dotnet add package GraphQL.Client.Serializer.Newtonsoft
dotnet add package Microsoft.Extensions.Options.ConfigurationExtensions

2.a. Alternative: Use GraphQL code generation (recommended)

Use StrawberryShake for better type safety, IntelliSense, and automatic class generation.

2.a.i. Install StrawberryShake tools

dotnet new tool-manifest
dotnet tool install StrawberryShake.Tools

2.a.ii. Add StrawberryShake packages

dotnet add package StrawberryShake.AspNetCore
dotnet add package StrawberryShake.CodeGeneration.CSharp.Analyzers

2.a.iii. Set up StrawberryShake

<!-- MyOptimizelyApp.csproj -->
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="StrawberryShake.AspNetCore" Version="13.9.0" />
    <PackageReference Include="StrawberryShake.CodeGeneration.CSharp.Analyzers" Version="13.9.0" />
  </ItemGroup>

  <ItemGroup>
    <GraphQL Include="**/*.graphql" />
  </ItemGroup>

</Project>

2b. Create GraphQL schema and queries

Download the schema and create queries based on the Alloy template.

# Download schema
dotnet graphql download https://cg.optimizely.com/content/v2?auth=3JCcia5gTU2RTiOv2aUCXu1ktYJjqzGgjXh2AVMFvxPyhJOY -f schema.graphql

Queries/ArticleQueries.graphql

This query is used to retrieve a list of articles from the database.

query GetArticles($limit: Int = 10, $skip: Int = 0) {
  ArticlePage(orderBy: { _modified: DESC }, limit: $limit, skip: $skip) {
    items {
      ContentLink { Id }
      Name
      RelativePath
      TeaserText
      PageImage { Url }
      MainBody
    }
    total
  }
}

query GetArticleById($id: String!) {
  ArticlePage(where: { ContentLink: { Id: { eq: $id } } }) {
    items {
      ContentLink { Id }
      Name
      RelativePath
      TeaserText
      PageImage { Url }
      MainBody
      _modified
    }
  }
}

Set up GraphQL client

Create a .graphqlrc.json configuration file.

{
  "schema": "schema.graphql",
  "documents": "Queries/**/*.graphql",
  "extensions": {
    "strawberryShake": {
      "name": "OptimizelyGraphClient",
      "namespace": "MyOptimizelyApp.GraphQL",
      "url": "https://cg.optimizely.com/content/v2?auth=3JCcia5gTU2RTiOv2aUCXu1ktYJjqzGgjXh2AVMFvxPyhJOY"
    }
  }
}

3. Configure services

Option A: Manual GraphQL client (basic approach)

// Program.cs
using GraphQL.Client.Http;
using GraphQL.Client.Serializer.Newtonsoft;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<GraphQLHttpClient>(provider =>
{
    var client = new GraphQLHttpClient(
        "https://cg.optimizely.com/content/v2?auth=3JCcia5gTU2RTiOv2aUCXu1ktYJjqzGgjXh2AVMFvxPyhJOY",
        new NewtonsoftJsonSerializer()
    );
    return client;
});

builder.Services.AddControllers();

var app = builder.Build();

app.UseRouting();
app.MapControllers();
app.Run();

Option B: StrawberryShake client (recommended approach)

// Program.cs
using MyOptimizelyApp.GraphQL;

var builder = WebApplication.CreateBuilder(args);

builder.Services
    .AddOptimizelyGraphClient()
    .ConfigureHttpClient(client => 
    {
        client.BaseAddress = new Uri("https://cg.optimizely.com/content/v2?auth=3JCcia5gTU2RTiOv2aUCXu1ktYJjqzGgjXh2AVMFvxPyhJOY");
    });

builder.Services.AddControllers();

var app = builder.Build();

app.UseRouting();
app.MapControllers();
app.Run();

4. Configure settings

You can store the endpoint configuration in appsettings.json.

{
  "OptimizelyGraph": {
    "Endpoint": "https://cg.optimizely.com/content/v2?auth=3JCcia5gTU2RTiOv2aUCXu1ktYJjqzGgjXh2AVMFvxPyhJOY"
  }
}

5. Create models

You have the option of creating models using either the manual approach or the StrawberryShake client.

With StrawberryShake, you also get:

  • Full IntelliSense support – Helps developers discover fields, arguments, and schema types directly in the editor. This reduces errors and accelerates development because you see what is available as you type.
  • Compile-time type checking – Ensures that queries match the GraphQL schema before the application runs. This prevents runtime failures and reduces debugging time by catching issues during build.
  • Automatic serialization and deserialization – Converts GraphQL responses into strongly typed C# objects without manual parsing. This improves reliability, removes boilerplate code, and makes the data easier to use in controllers and services.

Option A: Manual models (basic approach)

// Models/Article.cs
public class Article
{
    public ContentLink ContentLink { get; set; }
    public string Name { get; set; }
    public string RelativePath { get; set; }
    public string TeaserText { get; set; }
    public MediaReference PageImage { get; set; }
    public string MainBody { get; set; }
}

public class ContentLink { public string Id { get; set; } }

public class MediaReference { public string Url { get; set; } }

public class ContentResponse
{
    public List<Article> Items { get; set; } = new();
    public int Total { get; set; }
}

public class GraphQLResponse<T>
{
    public T ArticlePage { get; set; }
}

Option B: Auto-generated types with StrawberryShake (recommended approach)

When using StrawberryShake, types are automatically generated based on your GraphQL queries.

dotnet build

The generated types are available in the MyOptimizelyApp.GraphQL namespace, including:

  • IGetArticlesResult
  • IGetArticleByIdResult
  • GetArticlesQuery

6. Create a controller

Create a controller in your application to fetch content from Optimizely Graph. You can use either a manual GraphQL client or a strongly typed StrawberryShake client.

Option A: Manual GraphQL client

The manual client sends a raw GraphQL query and returns the response from Optimizely Graph.

// Controllers/ArticlesController.cs
using GraphQL;
using GraphQL.Client.Http;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/[controller]")]
public class ArticlesController : ControllerBase
{
    private readonly GraphQLHttpClient _graphQLClient;

    public ArticlesController(GraphQLHttpClient graphQLClient)
    {
        _graphQLClient = graphQLClient;
    }

    [HttpGet]
    public async Task<ActionResult<ArticlePageResponse>> GetArticles()
    {
        var query = @"
            query ListArticles {
                ArticlePage(orderBy: { _modified: DESC }, limit: 10) {
                    items {
                        ContentLink { Id }
                        Name
                        RelativePath
                        TeaserText
                        PageImage { Url }
                        MainBody
                    }
                    total
                }
            }";

        var request = new GraphQLRequest { Query = query };
        var response = await _graphQLClient.SendQueryAsync<GraphQLResponse<ContentResponse>>(request);

        if (response.Errors?.Any() == true)
        {
            return BadRequest(response.Errors);
        }

        return Ok(response.Data.ArticlePage);
    }
}

Option B: StrawberryShake client (strongly-typed)

The StrawberryShake client generates types from your schema and provides a strongly typed experience for querying Optimizely Graph.

// Controllers/ArticlesController.cs
using Microsoft.AspNetCore.Mvc;
using MyOptimizelyApp.GraphQL;

[ApiController]
[Route("api/[controller]")]
public class ArticlesController : ControllerBase
{
    private readonly IOptimizelyGraphClient _graphQLClient;

    public ArticlesController(IOptimizelyGraphClient graphQLClient)
    {
        _graphQLClient = graphQLClient;
    }

    [HttpGet]
    public async Task<ActionResult> GetArticles([FromQuery] int limit = 10, [FromQuery] int skip = 0)
    {
        var result = await _graphQLClient.GetArticles.ExecuteAsync(limit, skip);

        if (result.IsErrorResult())
        {
            return BadRequest(result.Errors);
        }

        return Ok(new
        {
            items = result.Data?.ArticlePage?.Items?.Select(article => new
            {
                id = article?.ContentLink?.Id,
                name = article?.Name,
                relativePath = article?.RelativePath,
                teaserText = article?.TeaserText,
                imageUrl = article?.PageImage?.Url,
                mainBody = article?.MainBody
            }),
            total = result.Data?.ArticlePage?.Total
        });
    }

    [HttpGet("{id}")]
    public async Task<ActionResult> GetArticleById(string id)
    {
        var result = await _graphQLClient.GetArticleById.ExecuteAsync(id);

        if (result.IsErrorResult())
        {
            return BadRequest(result.Errors);
        }

        var article = result.Data?.ArticlePage?.Items?.FirstOrDefault();
        if (article == null)
        {
            return NotFound();
        }

        return Ok(new
        {
            id = article.ContentLink?.Id,
            name = article.Name,
            relativePath = article.RelativePath,
            teaserText = article.TeaserText,
            imageUrl = article.PageImage?.Url,
            mainBody = article.MainBody,
            modified = article._modified
        });
    }
}

7. Test the API

dotnet build
dotnet ru

Visit:

  • https://localhost:7000/api/articles – Retrieve all articles.
  • https://localhost:7000/api/articles/123 – Retrieve an article by ID.

Example generated types

// Auto-generated by StrawberryShake
namespace MyOptimizelyApp.GraphQL
{
    public partial interface IGetArticlesResult
    {
        public IGetArticles_ArticlePage? ArticlePage { get; }
    }

    public partial interface IGetArticles_ArticlePage
    {
        public IReadOnlyList<IGetArticles_ArticlePage_Items?>? Items { get; }
        public int Total { get; }
    }

    public partial interface IGetArticles_ArticlePage_Items
    {
        public IGetArticles_ArticlePage_Items_ContentLink? ContentLink { get; }
        public string? Name { get; }
        public string? RelativePath { get; }
        public string? TeaserText { get; }
        public IGetArticles_ArticlePage_Items_PageImage? PageImage { get; }
        public string? MainBody { get; }
    }
}