Contentstack Delivery .Net SDK | V3

View as Markdown

Overview

This reference covers the public API of the contentstack.csharp package, with one page per class. Use it to look up method signatures, parameters, return types, validation behavior, and worked examples for every object you reach from a ContentstackClient.

Before you begin This reference assumes you have installed and initialized the SDK. If you have not, complete the .NET Delivery SDK Get Started guide first. This SDK requires .NET 10 or later, because the package targets net10.0. This reference documents version 3.2.0, and the changelog lists behavior changes between releases.

Minimum Working Example

This snippet creates a client, fetches one entry of a content type, and prints its title.

using System;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;

ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");

try
{
    Entry entry = await client.ContentType("<CONTENT_TYPE_UID>").Entry("<ENTRY_UID>").Fetch<Entry>();
    Console.WriteLine(entry.Title);
}
catch (ContentstackException e)
{
    // Thrown when the API rejects the request
    Console.WriteLine($"Request failed: {(int)e.StatusCode} {e.ErrorCode} {e.Message}");
}

SDK Structure

The SDK uses a builder pattern. ContentstackClient stores your credentials and settings, and each call from it returns an object for a narrower scope, such as a content type, an entry, or a query. A terminal call sends the request: Fetch for a single item, Find or FetchAll for a list, Count for a total, and the Sync methods for bulk retrieval. Every chain starts at the ContentstackClient.

Because option methods store a value on the object and send nothing, an invalid argument fails on the terminal call, which throws a ContentstackException. An option method that checks its own input throws at once instead, for example on a second call that writes the same parameter. Each method page lists the exceptions it throws.

Every terminal call is async and returns a Task, so await it. The generic calls, Fetch<T> and Find<T>, bind the response to the model class you pass. Pass Entry to read fields with Get instead.

Class Overview

Set up the client.

ClassRoleAccessed via
ContentstackClientOne stack and environment, and the start of every chain.new ContentstackClient(...)
ContentstackOptionsEvery client setting in one object, including region, branch, timeout, and proxy.new ContentstackOptions()
ContentstackRegionThe region that hosts the stack.ContentstackRegion.EU
LivePreviewConfigThe live preview settings of a client.options.LivePreview
EndpointA static resolver of Contentstack service URLs for a region.Endpoint.GetContentstackEndpoint(region, service)

Read content types and entries.

ClassRoleAccessed via
ContentTypeOne content type and its schema.client.ContentType(uid)
EntryOne entry of a content type.contentType.Entry(uid)
QueryA filtered list of the entries of one content type.contentType.Query()

Read assets and global fields.

ClassRoleAccessed via
AssetOne published asset.client.Asset(uid)
AssetLibraryThe list of published assets in the stack.client.AssetLibrary()
GlobalFieldOne global field and its schema.client.GlobalField(uid)
GlobalFieldQueryThe list of global fields in the stack.client.GlobalFieldQuery()

Read taxonomies.

ClassRoleAccessed via
TaxonomyPublished taxonomies, and the entries tagged with their terms.client.Taxonomies()
TermOne published term of a taxonomy.taxonomy.Term(uid)
TermQueryThe list of published terms of one taxonomy.taxonomy.Terms()

Sync content, and read results and errors.

ClassRoleAccessed via
SyncStackThe result of a sync request, with its tokens.client.SyncRecursive()
SyncTypeThe event types a sync returns.SyncType.EntryPublished
ContentstackCollectionThe items a list request returns, with the total count when you request it.query.Find<T>()
ContentstackExceptionThe exception a failed request throws.catch (ContentstackException e)

Task Index

TaskStart hereClass
Fetch one entry by UIDFetchEntry
Filter entries by a field valueWhereQuery
Page through the entries of a content typeSkipQuery
Return referenced entries in fullIncludeReferenceEntry
Read an entry in another localeSetLocaleEntry
Read an entry variantVariantEntry
List the published assets in the stackFetchAllAssetLibrary
Filter entries by a taxonomy term and the terms below itEqualAndBelowTaxonomy
Read the schema of a content typeFetchContentType
Sync all published content, then only what changedSyncRecursiveContentstackClient
Show draft content in live previewLivePreviewQueryAsyncContentstackClient
Set the region, branch, timeout, or proxyContentstackOptionsContentstackOptions

Key Usage Patterns

Each example below is a complete C# file with its own using directives and client, so you can run it as a top-level program.

Filter and sort entries into your own model class

using System;
using System.Linq;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;

ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");

try
{
    // The filter and sort methods store values. Find sends the request.
    ContentstackCollection<Article> result = await client.ContentType("<CONTENT_TYPE_UID>")
        .Query()
        .Exists("url")
        .Descending("updated_at")
        .Limit(10)
        .IncludeCount()
        .Find<Article>();

    Console.WriteLine($"Showing {result.Items.Count()} of {result.Count}");
    foreach (Article article in result)
    {
        Console.WriteLine($"{article.Title}: {article.Url}");
    }
}
catch (ContentstackException e)
{
    Console.WriteLine(e.Message);
}

// Property names bind to response fields without regard to case.
public class Article
{
    public string Uid { get; set; }
    public string Title { get; set; }
    public string Url { get; set; }
}

Page through every entry of a content type

The SDK sends one request per Find call and does not request further pages. Build a new query for each page, and stop at the Count that IncludeCount adds.

using System;
using System.Collections.Generic;
using System.Linq;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;

ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");

const int pageSize = 25;
List<Entry> all = new List<Entry>();
int total = 0;

try
{
    do
    {
        // A new query per page, because Skip and Limit throw on a second call.
        ContentstackCollection<Entry> page = await client.ContentType("<CONTENT_TYPE_UID>")
            .Query()
            .Skip(all.Count)
            .Limit(pageSize)
            .IncludeCount()
            .Find<Entry>();

        List<Entry> items = page.Items.ToList();
        all.AddRange(items);
        total = page.Count;
        if (items.Count == 0) break;
    } while (all.Count < total);

    Console.WriteLine($"Read {all.Count} of {total} entries");
}
catch (ContentstackException e)
{
    Console.WriteLine(e.Message);
}

Sync all published content, then only what changed

using System;
using System.Linq;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;

ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");

try
{
    // SyncRecursive reads every page before it returns.
    SyncStack initial = await client.SyncRecursive();
    Console.WriteLine($"Published items: {initial.Items.Count()}");

    // Store SyncToken, then pass it on a later run to receive only the changes.
    SyncStack changes = await client.SyncToken(initial.SyncToken);
    Console.WriteLine($"Changed since the first sync: {changes.Items.Count()}");
}
catch (ContentstackException e)
{
    Console.WriteLine(e.Message);
}

Register the client with dependency injection in ASP.NET Core

AddContentstack, from the contentstack.aspnetcore package, reads the ContentstackOptions section of your configuration and registers ContentstackClient as a transient service. In an ASP.NET Core app, pass builder.Configuration, which reads that section from appsettings.json. Each key in the section matches a ContentstackOptions property, for example ApiKey, DeliveryToken, and Environment.

The file below registers the client and a service class that receives it through its constructor.

using System;
using System.Threading.Tasks;
using Contentstack.AspNetCore;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

public static class ContentstackSetup
{
    // Call from Program.cs: ContentstackSetup.AddServices(builder.Services, builder.Configuration)
    public static void AddServices(IServiceCollection services, IConfigurationRoot configuration)
    {
        services.AddContentstack(configuration);
        services.AddTransient<ArticleService>();
    }
}

// The container passes a new ContentstackClient built from the bound options.
public class ArticleService
{
    private readonly ContentstackClient _client;

    public ArticleService(ContentstackClient client)
    {
        _client = client;
    }

    public async Task<string> GetTitleAsync(string entryUid)
    {
        try
        {
            Entry entry = await _client.ContentType("<CONTENT_TYPE_UID>").Entry(entryUid).Fetch<Entry>();
            return entry.Title;
        }
        catch (ContentstackException e)
        {
            Console.WriteLine(e.Message);
            return null;
        }
    }
}

Retry a rate-limited request with a delay

The SDK sends each request once. Catch a 429 status and send the request again after a delay that grows with each attempt.

using System;
using System.Net;
using System.Threading.Tasks;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;

ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");

const int maxAttempts = 3;
Entry entry = null;

for (int attempt = 1; attempt <= maxAttempts && entry == null; attempt++)
{
    try
    {
        entry = await client.ContentType("<CONTENT_TYPE_UID>").Entry("<ENTRY_UID>").Fetch<Entry>();
    }
    catch (ContentstackException e) when (e.StatusCode == HttpStatusCode.TooManyRequests && attempt < maxAttempts)
    {
        // Wait one second, then two, before the next attempt.
        await Task.Delay(TimeSpan.FromSeconds(attempt));
    }
    catch (ContentstackException e)
    {
        Console.WriteLine(e.Message);
        break;
    }
}

Console.WriteLine(entry?.Title);

Tell a network failure from an API rejection

A timeout or DNS failure produces no API error body, so ErrorCode stays 0 and StatusCode reports 500 although no server sent it. Check ErrorCode before you trust StatusCode.

using System;
using Contentstack.Core;
using Contentstack.Core.Internals;
using Contentstack.Core.Models;

ContentstackClient client = new ContentstackClient("<API_KEY>", "<DELIVERY_TOKEN>", "<ENVIRONMENT>");

try
{
    Entry entry = await client.ContentType("<CONTENT_TYPE_UID>").Entry("<ENTRY_UID>").Fetch<Entry>();
    Console.WriteLine(entry.Title);
}
catch (ContentstackException e) when (e.ErrorCode == 0)
{
    // No API error body: a timeout, a DNS failure, or a response the SDK could not process
    Console.WriteLine($"Request did not complete: {e.Message}");
}
catch (ContentstackException e)
{
    // The API answered and rejected the request
    Console.WriteLine($"API error: {(int)e.StatusCode} {e.ErrorCode} {e.Message}");
}

SDK-Wide Notes

ConcernBehaviorDefault when unset
AuthenticationThe ContentstackClient constructor takes the API key, delivery token, and environment, and every object the client creates sends them. No other method takes credentials.Not applicable. The constructor does not validate the three values, so a missing or wrong value fails at the first request.
Error surfacingEvery request method throws a ContentstackException, or one of its subclasses, when the request fails. Read Message, ErrorCode, StatusCode, and Errors. The ContentstackException class page lists which call throws which subclass.Not applicable. A timeout or DNS failure throws with StatusCode 500 and ErrorCode 0.
Regions and endpointsSet Region on ContentstackOptions, or pass region to the constructor: US, EU, AU, AZURE_NA, AZURE_EU, GCP_NA, or GCP_EU. The client resolves the delivery host for the region through the Endpoint class, from regions.json. A Host value overrides the region.ContentstackRegion.US, which resolves to cdn.contentstack.io.
BranchesSet Branch on ContentstackOptions. The SDK sends it as the branch header on every request.No branch header, so the API applies its default.
Rate limiting and retryThis SDK does not auto-retry on 429, so implement backoff in your calling code. It also sends a request once when the response is a 5xx status or the request times out.No retry.
TimeoutsSet Timeout on ContentstackOptions, or pass timeout to the constructor, in milliseconds. A request that times out throws a ContentstackException with StatusCode 500 and ErrorCode 0.30000 ms.
Proxy configurationSet Proxy on ContentstackOptions, or pass proxy to the constructor. Known issue: Query.Find, Query.FindOne, and Query.Count do not use it. See the Query class page.null.
Request URL encodingThe SDK adds query values to the request URL without URL encoding them. A value that holds & or # changes the request URL, so avoid those characters in filter and parameter values.Not applicable. The SDK never encodes values.
Option methodsOption methods store their value on the object that sends the request, and no method removes a stored option. On most classes, a second call that writes the same parameter throws. Build a new object for each request with different options.Each new object starts with no options.
JSON bindingThe SDK parses responses with System.Text.Json. Fetch<T> and Find<T> bind response fields to your model class without regard to case. Add converters to client.SerializerOptions.Converters before the first request.SerializerOptions with case-insensitive property names and the SDK's own converters.
Dependency injectionservices.AddContentstack(configuration), from the contentstack.aspnetcore package, binds the ContentstackOptions configuration section and registers ContentstackClient as a transient service.Not applicable. Without it, create the client with a constructor.

Warning This SDK requires a delivery token, which the client sends as the access_token header. The constructor does not check the token, so a management token or a preview token in its place fails at the first request with a ContentstackException. Pass a preview token in LivePreviewConfig.PreviewToken instead. Refer to Content Delivery API Errors for the full error code list.

SDK Limitations

CapabilitySupportedNotes / Alternative
Retry a rate-limited or failed requestNoCatch ContentstackException, check StatusCode, and send the request again after a delay, as in Key Usage Patterns above.
Read every page of a list in one callNoLoop with Skip and Limit on a new query per page, as in Key Usage Patterns above. SyncRecursive is the exception, and reads every page of a sync.
Reuse one request object for a request with different optionsNoMost option methods throw on a second call. Build a new object from the client for each request.
Encode special characters in query valuesNoAvoid & and # in filter and parameter values.