Skip to content

Repository files navigation

StoryblokSharp

A modern .NET client for the Storyblok Headless CMS API. This project is a C# port of the official storyblok-js-client, maintaining feature parity while providing a strongly-typed interface for .NET applications. It supports both Storyblok's Content Delivery API (v2) and Management API (v1).

Rationale

As a personal project be kind! This is shared as is and is not for production.

Acknowledgements

This project is a port of the storyblok-js-client JavaScript library, created and maintained by the Storyblok team (@storyblok). We are grateful for their excellent work which serves as the foundation for this .NET implementation.

Additionally, we've included the richtext implementation of richtext Javascript library, used to interact with the richtext components.

License: This project, like the original storyblok-js-client, is licensed under MIT.

Original Client Repository: https://github.com/storyblok/storyblok-js-client Original Richtext Repository: https://github.com/storyblok/richtext

Heritage

This library is based on the official storyblok-js-client JavaScript library and follows the same core principles and features. We've tried to carefully translated the JavaScript implementation to idiomatic C#, taking advantage of .NET's type system and modern features while maintaining compatibility with Storyblok's APIs. Key aspects we've preserved include:

  • Same configuration options and initialization patterns
  • Compatible caching mechanisms
  • Equivalent rate limiting and retry logic
  • Parallel rich text rendering capabilities
  • Similar helper utilities and extension methods

Features

  • 🚀 Built for modern .NET (targets .NET 9.0)
  • 💪 Strongly typed story content
  • 🔄 Automatic rate limiting and retry handling
  • 💾 Configurable caching
  • 🔍 Rich text rendering
  • 🔒 Thread-safe operations
  • 📦 Available as a NuGet package
  • 🔗 Dependency injection friendly
  • 🏎️ Async/await support

Also:

  • 💡Support for both published and draft content
  • 💡Comprehensive story querying and filtering
  • 💡Rich text rendering with customizable schema
  • 💡Built-in caching with memory and custom providers
  • 💡Rate limiting and retry handling
  • 💡Blazor component integration
  • 💡Image optimization
  • 💡HTML sanitization

Installation

Install via Github packages - NuGet:

dotnet add package StoryblokSharp

The NuGet package is hosted in this GitHub repository's package registry.

Story Retrieval

Single Story

Retrieve a single story by its slug:

// Get a published storyvarstory=await_client.GetStoryAsync<HomeContent>("home",newStoryQueryParameters{Version="published"});// Get a draft storyvardraftStory=await_client.GetStoryAsync<HomeContent>("home",newStoryQueryParameters{Version="draft"});// Get a story in a specific languagevargermanStory=await_client.GetStoryAsync<HomeContent>("home",newStoryQueryParameters{Version="published",Language="de"});

Multiple Stories

Retrieve multiple stories with filtering and pagination:

// Get all published storiesvarstories=await_client.GetStoriesAsync<BlogPost>(newStoryQueryParameters{Version="published",PerPage=10,Page=1});// Get stories with specific tagsvartaggedStories=await_client.GetStoriesAsync<BlogPost>(newStoryQueryParameters{WithTag="featured",SortBy="created_at:desc"});// Get all stories (handles pagination automatically)varallStories=await_client.GetAllAsync<BlogPost>("cdn/stories",newStoryQueryParameters{StartsWith="blog/"});

Query Parameters

The StoryQueryParameters class provides various filtering and sorting options:

varparameters=newStoryQueryParameters{// Version controlVersion="published",// or "draft"// PaginationPerPage=10,Page=1,// FilteringStartsWith="blog/",WithTag="featured",SearchTerm="tutorial",ExcludingFields="body,image",// SortingSortBy="created_at:desc",// LanguageLanguage="en",FallbackLang="de",// RelationsResolveLinks="1",ResolveRelations=new[]{"author","categories"},ResolveLevel=2};

Working with Content Types

You can create strongly-typed models for your content:

publicclassBlogPost{publicstringTitle{get;set;}publicstringSlug{get;set;}publicRichTextFieldContent{get;set;}publicAssetFeaturedImage{get;set;}publicDateTimePublishedDate{get;set;}publicstring[]Tags{get;set;}}// Use the typed modelvarresponse=await_client.GetStoryAsync<BlogPost>("my-blog-post");vartitle=response.Story.Content.Title;varcontent=response.Story.Content.Content;

Basic Usage

// Configure servicesservices.AddStoryblokClient(builder =>builder.WithAccessToken("your_access_token").WithCache(options =>options.WithType(CacheType.Memory).WithDefaultExpiration(TimeSpan.FromMinutes(5))).WithMaxRetries(3).WithRateLimit(5));// Inject and use the clientpublicclassMyService{privatereadonlyIStoryblokClient_client;publicMyService(IStoryblokClientclient){_client=client;}publicasyncTask<Story<T>>GetStoryAsync<T>()whereT:class{varparameters=newStoryQueryParameters{Version="published",Language="en"};varresponse=await_client.GetStoryAsync<T>("home",parameters);returnresponse.Story;}}

Advanced Configuration

The client can be configured with various options:

services.AddStoryblokClient(builder =>builder.WithAccessToken("your_access_token").WithOAuthToken("your_oauth_token")// For management API.WithRegion(Region.EU).WithHttps().WithMaxRetries(3).WithTimeout(30).WithRateLimit(5).WithHeaders(newDictionary<string,string>{["Custom-Header"]="Value"}).WithCache(options =>options.WithType(CacheType.Memory).WithDefaultExpiration(TimeSpan.FromMinutes(5))).WithCustomCache(newYourCustomCacheProvider()).WithRichTextSchema(newYourCustomSchema()).WithComponentResolver((type,props)=>$"<div>{type}</div>").WithResponseInterceptor(async response =>{// Custom response handlingreturnresponse;}));

Rich Text Rendering

The library includes a powerful rich text renderer with support for custom resolvers:

services.Configure<RichTextOptions>(options =>{options.OptimizeImages=true;options.KeyedResolvers=true;options.InvalidNodeHandling=InvalidNodeStrategy.Remove;options.MarkSortPriority=new[]{MarkTypes.Bold,MarkTypes.Italic,MarkTypes.Link};});

Blazor Integration

StoryblokSharp provides seamless integration with Blazor components:

// Register Blazor component resolverservices.AddBlazorComponentResolver();// Register a componentservices.AddStoryblokComponent<HeroComponent>("hero");// Create a Blazor componentpublicclassHeroComponent:ComponentBase,IComponent{[Parameter]publicstringTitle{get;set;}[Parameter]publicstringSubtitle{get;set;}protectedoverridevoidBuildRenderTree(RenderTreeBuilderbuilder){builder.OpenElement(0,"div");builder.AddAttribute(1,"class","hero");builder.OpenElement(2,"h1");builder.AddContent(3,Title);builder.CloseElement();builder.OpenElement(4,"p");builder.AddContent(5,Subtitle);builder.CloseElement();builder.CloseElement();}}// Use in Storyblok content
@{varstory=awaitStoryblokClient.GetStoryAsync<dynamic>("home");varcomponent=story.Story.Content.hero;}<StoryblokComponentType="hero"Props="@component"/>

Image Optimization

Configure image optimization options:

services.Configure<RichTextOptions>(options =>{options.OptimizeImages=true;options.ImageOptions=newImageOptimizationOptions{Width=800,Height=600,Loading="lazy",Class="optimized-image",SrcSet=new[]{newSrcSetEntry{Width=400},newSrcSetEntry{Width=800},newSrcSetEntry{Width=1200}},Sizes=new[]{"(max-width: 400px) 100vw","(max-width: 800px) 50vw","800px"},Filters=newImageFilters{Quality=80,Format="webp",Grayscale=false}}});

Security

The library includes built-in HTML sanitization:

services.Configure<HtmlSanitizerOptions>(options =>{options.AllowedTags.Add("custom-tag");options.AllowedAttributes["a"].Add("rel");options.AllowedProtocols.Add("tel");});

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

You can customize story queries with various parameters:

varparameters=newStoryQueryParameters{Version="draft",// 'draft' or 'published'Language="en",// Language codeResolveLinks="story",// How to resolve linksResolveRelations=new[]{"author"},// Relations to resolveExcludingFields="long_text",// Fields to excludeSort_by="position:desc",// Sort orderStartsWith="blog/",// Filter by pathWithTag="featured",// Filter by tagPage=1,// Page numberPerPage=10// Items per page};varstories=await_storyblok.GetStoriesAsync<BlogPostContent>(parameters);

Security

The library includes built-in HTML sanitization:

services.Configure<HtmlSanitizerOptions>(options =>{options.AllowedTags.Add("custom-tag");options.AllowedAttributes["a"].Add("rel");options.AllowedProtocols.Add("tel");});

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

License

This project is licensed under the MIT License - see the LICENSE file for details.

Support

About

Dotnet implementation of Storyblok client

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages