StrapiConnect 1.1.1

There is a newer version of this package available.
See the version list below for details.
dotnet add package StrapiConnect --version 1.1.1
                    
NuGet\Install-Package StrapiConnect -Version 1.1.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="StrapiConnect" Version="1.1.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="StrapiConnect" Version="1.1.1" />
                    
Directory.Packages.props
<PackageReference Include="StrapiConnect" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add StrapiConnect --version 1.1.1
                    
#r "nuget: StrapiConnect, 1.1.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package StrapiConnect@1.1.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=StrapiConnect&version=1.1.1
                    
Install as a Cake Addin
#tool nuget:?package=StrapiConnect&version=1.1.1
                    
Install as a Cake Tool

StrapiConnect

StrapiConnect is a lightweight .NET library that simplifies integration with a Strapi CMS backend using a resilient, pre-configured HttpClient. It includes built-in support for authentication and transient-fault handling using Polly.

Features

  • 📦 Easily registers a typed HttpClient for IStrapiConnect
  • 🔒 Supports Bearer Token authentication from config
  • 🔁 Polly retry policy with exponential backoff
  • 🚫 Validates BaseUrl config at startup
  • 💡 Clean DI extension for Program.cs or Startup.cs

Installation

Install via NuGet:

dotnet add package StrapiConnect

Usage

  1. Add Configuration to appsettings.json
"StrapiConnect": {
  "BaseUrl": "https://your-strapi-instance/api",
  "ApiKey": "your-secret-api-token"
}
  1. Register the Service in Program.cs
builder.Services.AddStrapiService(builder.Configuration);
  1. Inject and Use IStrapiService
using StrapiConnect;

var strapiConnect = new StrapiConnect("http://localhost:1337/api");
var request = new FindManyRequest("articles");
var result = await strapiConnect.ExecuteAsync<ArticleResponse>(request);
Console.WriteLine(result);

Strapi 5 Support

StrapiConnect works with both Strapi v4 and Strapi 5. The defaults are backward compatible: existing code keeps requesting the legacy v4 response shape, so no changes are required when upgrading the package.

Response format (v4 vs v5)

By default the client sends the Strapi-Response-Format: v4 header, so a Strapi 5 server replies in the legacy shape (fields nested under data.attributes). To opt into the native Strapi 5 flattened shape (fields directly on data, with documentId), pass StrapiResponseFormat.V5:

using StrapiConnect.Enums;

// Native Strapi 5 response (flattened, no `attributes` wrapper)
var result = await strapiConnect.ExecuteAsync<ArticleResponse>(
    "http://localhost:1337/api", request, StrapiResponseFormat.V5);

// Legacy v4 response (default — equivalent to the call above without the enum)
var legacy = await strapiConnect.ExecuteAsync<ArticleResponse>(
    "http://localhost:1337/api", request, StrapiResponseFormat.V4);

Fetch a single document by documentId

Strapi 5 addresses single records by their string documentId:

var request = new FindByDocumentIdRequest("articles", "znrlzntu9ei5onjvwfaalu2v");
var article = await strapiConnect.ExecuteAsync<ArticleResponse>(
    "http://localhost:1337/api", request, StrapiResponseFormat.V5);

FindByIdRequest(contentType, int id) remains available for v4-style numeric ids.

Create / update with relations

The client now supports POST / PUT / DELETE. Relations are managed with RelationBuilder (Connect / Disconnect / Set, with optional positioning), following the Strapi 5 relations API:

// Create an article and link categories
var create = new CreateRequest("articles")
    .WithBody(new RequestBody()
        .Field("title", "Hello world")
        .Relation("categories", new RelationBuilder().Connect("cat1").Connect("cat2")));

await strapiConnect.ExecuteAsync<ArticleResponse>(
    "http://localhost:1337/api", create, StrapiResponseFormat.V5);

// Update: connect with ordering, and disconnect another relation
var update = new UpdateRequest("articles", "znrlzntu9ei5onjvwfaalu2v")
    .WithBody(new RequestBody()
        .Relation("categories", new RelationBuilder()
            .Connect("cat3", RelationPosition.Start())
            .Disconnect("cat1")));

await strapiConnect.ExecuteAsync<ArticleResponse>(
    "http://localhost:1337/api", update, StrapiResponseFormat.V5);

// Replace all relations (cannot be combined with connect/disconnect)
var replace = new UpdateRequest("articles", "znrlzntu9ei5onjvwfaalu2v")
    .WithBody(new RequestBody()
        .Relation("categories", new RelationBuilder().Set("cat2", "cat3")));

// Delete by documentId (204 No Content returns null)
var delete = new DeleteRequest("articles", "znrlzntu9ei5onjvwfaalu2v");
await strapiConnect.ExecuteAsync<object>("http://localhost:1337/api", delete);

Positioning options: RelationPosition.Before(id), RelationPosition.After(id), RelationPosition.Start(), RelationPosition.End() (default).

Populate (and deep populate)

Strapi 5's native populate=* is only one level deep, so nested components/relations/media are not returned by it. There are three ways to get deeper data:

// 1. Everything, one level
request.PopulateAll();                               // populate=*

// 2. Explicit nesting (relations / single components) — any depth
request.Populate("author").Populate("avatar").PopulateAll();
// => populate[author][populate][avatar][populate]=*

// 3. Dynamic zones (e.g. `blocks`) — MUST use the per-component `on` form.
//    Strapi returns HTTP 400 for populate[blocks][populate][<field>]=...
var blocks = request.Populate("blocks");
blocks.OnComponent("blocks.hero").PopulateAll();
blocks.OnComponent("blocks.feature-block").Populate("features").PopulateAll();
// => populate[blocks][on][blocks.hero][populate]=*
//  & populate[blocks][on][blocks.feature-block][populate][features][populate]=*

Or, if your Strapi has the strapi-plugin-populate-deep plugin installed, populate everything to a depth in one call:

request.SetPopulateLevel(5);      // ?pLevel=5  (no-op if the plugin isn't installed)

Draft & Publish (status)

Strapi 5 removed the v4 publicationState parameter and replaced it with status. Use SetStatus to choose the draft or published version. With no call the Strapi default (published) applies.

using StrapiConnect.Enums;

request.SetStatus(StrapiStatus.Draft);       // ?status=draft
request.SetStatus(StrapiStatus.Published);   // ?status=published

Localization (locale)

request.SetLocale("en");      // ?locale=en

Filtering, sorting & pagination

// Equality emits the canonical Strapi filter form (a bare `field=value` is ignored
// by Strapi), and values are URL-encoded automatically.
request.Equal("slug", "my-post");                         // filters[slug][$eq]=my-post
request.Filter(FilterType.ContainsCaseInsensitive, "title", "hello"); // filters[title][$containsi]=hello
request.Filter(FilterType.Between, "price", "10,20");     // filters[price][$between][0]=10&[1]=20
request.Filter(FilterType.In, "category.slug", "tours");  // filters[category][slug][$in][0]=tours

// Multiple sorts produce distinct indices (sort[0], sort[1], …)
request.Sort("title", SortDirection.Ascending);           // sort[0]=title:asc
request.Sort("createdAt", SortDirection.Descending);      // sort[1]=createdAt:desc

request.SetPage(1);
request.SetPageSize(10);

Supported operators (via FilterType): $eq, $ne, $lt, $lte, $gt, $gte, $in, $notIn, $contains, $notContains, $startsWith, $endsWith, $null, $notNull, the case-insensitive variants $eqi/$nei/$containsi/$notContainsi/ $startsWithi/$endsWithi, and $between.

Error handling

By default a failed request (transport error, non-success status, or deserialization failure) is logged and returns default (null). To make failures throw instead, pass throwOnError: true; a StrapiRequestException (wrapping the original error) is raised:

var result = await strapiConnect.ExecuteAsync<ArticleResponse>(
    "http://localhost:1337/api", request, StrapiResponseFormat.V5, throwOnError: true);

Built-in Resilience with Polly

  • Retries 3 times on transient errors
  • Supports HttpStatusCode.TooManyRequests and SocketException
  • Exponential backoff (2s, 4s, 8s)

Releasing to NuGet

scripts/publish-nuget.sh sets the package version, builds, tests, packs and pushes the package in one step:

# Validate the whole pipeline without publishing (no API key needed)
./scripts/publish-nuget.sh 1.0.1 --dry-run

# Publish, then tag the release
NUGET_API_KEY=oy2... ./scripts/publish-nuget.sh 1.0.1 --tag

The version argument must be valid SemVer (1.0.1, 1.2.0-beta.1). It is written into src/StrapiConnect/StrapiConnect.csproj as <Version>; on a dry run or any failure the original value is restored, and after a successful push the bump is kept so you can commit it.

Option Purpose
--api-key <key> NuGet API key (default $NUGET_API_KEY)
--source <url> Push target (default $NUGET_SOURCE or nuget.org)
--output <dir> .nupkg output directory (default artifacts/nuget)
--configuration <cfg> Build configuration (default Release)
--skip-tests Skip dotnet test
--keep-version Publish the version already in the .csproj
--dry-run Build and pack only, no push
--tag Create and push git tag v<version> after publishing

Run ./scripts/publish-nuget.sh --help for the full reference. The Release NuGet Package GitHub Actions workflow remains available for publishing from CI.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.1.2 129 8/25/2026
1.1.1 94 8/24/2026
1.0.12 203 5/14/2026
1.0.11 1,631 9/19/2025
1.0.10 240 8/13/2025
1.0.9 219 7/14/2025
1.0.8 259 6/2/2025
1.0.7 617 5/24/2025
1.0.6 181 5/23/2025
1.0.5 196 5/23/2025
1.0.4 238 5/22/2025
1.0.3 232 5/22/2025
1.0.2 237 5/22/2025
1.0.1 233 5/22/2025
1.0.0 232 5/22/2025