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 MyOptimizelyApp2. 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.ConfigurationExtensions2.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.Tools2.a.ii. Add StrawberryShake packages
dotnet add package StrawberryShake.AspNetCore
dotnet add package StrawberryShake.CodeGeneration.CSharp.Analyzers2.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.graphqlQueries/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 buildThe generated types are available in the MyOptimizelyApp.GraphQL namespace, including:
IGetArticlesResultIGetArticleByIdResultGetArticlesQuery
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 ruVisit:
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; }
}
}Updated 1 day ago
