.NET

.NET is a Microsoft framework that provides a managed environment and libraries for building and running applications across web, desktop, mobile, and cloud platforms

.NET logo

What Is .NET?

.NET is Microsoft's open-source, cross-platform framework for building web applications, APIs, and services. ASP.NET Core, its web framework component, provides everything from Minimal APIs and MVC controllers to Blazor for interactive frontends—all running on a high-performance runtime with built-in dependency injection, middleware pipelines, and strongly-typed configuration.

.NET 10 is the current Long-Term Support (LTS) release with support extending to approximately November 2028, and .NET 9 remains in active Standard Term Support (STS) through November 2026. Both versions share the same HttpClient and IHttpClientFactory patterns used throughout this guide.

Why Integrate .NET with Strapi?

Pairing .NET with Strapi v5 bridges the gap between a strongly-typed, high-performance backend framework and a flexible, API-first content management system. Here's what that combination gives you:

  • Auto-generated REST endpoints—and, with Strapi's official GraphQL plugin installed, GraphQL queries and mutations—Define a content type in Strapi's Content-Type Builder, and production-ready API access appears immediately. No custom controller scaffolding required on the Strapi side.
  • Standard HttpClient consumption—Strapi's REST API returns JSON over HTTP. ASP.NET Core's IHttpClientFactory and System.Net.Http.Json extensions handle serialization, connection pooling, and resilience out of the box.
  • One content backend, multiple .NET frontends—A single Strapi instance can serve an ASP.NET Core MVC site, a Blazor frontend, and a .NET MAUI mobile app through the same API endpoints. No duplicate content models needed.
  • Full admin interface with RBAC—Strapi ships with a complete editorial UI, role-based access control, draft/publish workflows, and media management. Building equivalent functionality in ASP.NET Core from scratch is weeks of undifferentiated work.
  • Open-source and self-hostable—Deploy Strapi alongside your .NET application on Azure, AWS, or on-premises. No per-API-call pricing, no external data dependencies, and full control over data residency.
  • TypeScript codebase aligns with typed-language expectations—Strapi 5 runs on a codebase that is almost entirely written in TypeScript. When you need to extend Strapi through plugins or lifecycle hooks, you get the same autocompletion and type safety C# developers expect.

How to Integrate .NET with Strapi

Prerequisites

Before starting, make sure you have the following installed and configured:

ToolVersionPurpose
.NET SDK9.0.14+ (STS) or 10.x (LTS)ASP.NET Core runtime and CLI
Node.jsv20, v22, or v24Strapi runtime
npmv6+Strapi package management
Code editorVS Code, Visual Studio, or RiderDevelopment environment

Verify your installations:

dotnet --version
node --version
npm --version

Step 1: Create and Configure a Strapi v5 Project

Start by scaffolding a new Strapi project:

npx create-strapi@latest my-strapi-project

The CLI walks you through authentication with Strapi Cloud (optional) and project configuration. Press Enter to accept defaults for a quick setup.

Start the development server:

cd my-strapi-project && npm run develop

The Admin Panel opens at http://localhost:1337/admin. Register your first admin account.

Step 2: Create Content Types in Strapi

Navigate to Content-Type Builder in the admin sidebar. Create an "Article" Collection Type:

  1. Click the + icon next to Collection types
  2. Enter Article as the Display name and click Continue
  3. Add these fields:
Field TypeNameAdvanced Settings
Text (Short)titleRequired, Unique
Text (Long)slugRequired, Unique
Rich text (Blocks)content—
Media (Single)cover—
  1. Click Save and wait for Strapi to restart

Add a few sample articles through Content Manager, then click Publish on each entry to make them accessible via the API.

Step 3: Configure API Permissions

By default, Strapi's REST API returns nothing for unauthenticated requests. Configure public access:

  1. Go to Settings > Users & Permissions Plugin > Roles
  2. Click the Public role
  3. Under Permissions, find Article and check find and findOne
  4. Click Save

Test the endpoint:

curl http://localhost:1337/api/articles

The response uses Strapi v5's flattened format—attributes sit at the top level of each data item, not nested inside data.attributes:

{
  "data": [
    {
      "id": 1,
      "documentId": "wf7m1n3g8g22yr5k50hsryhk",
      "title": "Getting Started with Strapi",
      "slug": "getting-started-with-strapi",
      "createdAt": "2024-09-10T12:49:32.350Z",
      "updatedAt": "2024-09-10T13:14:18.275Z",
      "publishedAt": "2024-09-10T13:14:18.280Z",
      "locale": null
    }
  ],
  "meta": {
    "pagination": { "page": 1, "pageSize": 25, "pageCount": 1, "total": 1 }
  }
}

Note both id (integer) and documentId (alphanumeric string) appear in responses. Use documentId as your stable content identifier—it persists across localization variants and content versions.

Step 4: Generate an API Token

For authenticated access from your .NET application:

  1. Navigate to Settings > Global settings > API Tokens
  2. Click Create new API Token
  3. Configure the token:
FieldValue
Namedotnet-client
Token durationUnlimited (or rotate on a schedule)
Token typeRead-only
  1. Click Save and copy the token immediately—it's shown only once unless you've configured an encryption key.

Every request to Strapi's REST API includes this token in the Authorization header:

Authorization: bearer your-api-token-here

Step 5: Create the ASP.NET Core Project

Scaffold a new ASP.NET Core MVC application:

dotnet new mvc -n StrapiDotnetApp
cd StrapiDotnetApp

Add the resilience package for production-grade HTTP handling:

dotnet add package Microsoft.Extensions.Http.Resilience

Step 6: Define C# Models for Strapi v5 Responses

Strapi v5's flattened response format means your C# models map directly to the JSON structure—no intermediate attributes wrapper class needed.

Create a Models/ directory with the following files:

// Models/StrapiResponses.cs
using System.Text.Json.Serialization;

namespace StrapiDotnetApp.Models;

public class StrapiListResponse<T>
{
    [JsonPropertyName("data")]
    public List<T> Data { get; set; } = new();

    [JsonPropertyName("meta")]
    public StrapiMeta? Meta { get; set; }
}

public class StrapiSingleResponse<T>
{
    [JsonPropertyName("data")]
    public T? Data { get; set; }

    [JsonPropertyName("meta")]
    public StrapiMeta? Meta { get; set; }
}

public class StrapiMeta
{
    [JsonPropertyName("pagination")]
    public StrapiPagination? Pagination { get; set; }
}

public class StrapiPagination
{
    [JsonPropertyName("page")]
    public int Page { get; set; }

    [JsonPropertyName("pageSize")]
    public int PageSize { get; set; }

    [JsonPropertyName("pageCount")]
    public int PageCount { get; set; }

    [JsonPropertyName("total")]
    public int Total { get; set; }
}
// Models/Article.cs
using System.Text.Json.Serialization;

namespace StrapiDotnetApp.Models;

public class Article
{
    [JsonPropertyName("id")]
    public int Id { get; set; }

    [JsonPropertyName("documentId")]
    public string DocumentId { get; set; } = string.Empty;

    [JsonPropertyName("title")]
    public string? Title { get; set; }

    [JsonPropertyName("slug")]
    public string? Slug { get; set; }

    [JsonPropertyName("publishedAt")]
    public DateTimeOffset? PublishedAt { get; set; }

    [JsonPropertyName("createdAt")]
    public DateTimeOffset CreatedAt { get; set; }

    [JsonPropertyName("locale")]
    public string? Locale { get; set; }

    [JsonPropertyName("cover")]
    public StrapiImage? Cover { get; set; }
}

public class StrapiImage
{
    [JsonPropertyName("url")]
    public string Url { get; set; } = string.Empty;

    [JsonPropertyName("alternativeText")]
    public string? AlternativeText { get; set; }

    [JsonPropertyName("width")]
    public int Width { get; set; }

    [JsonPropertyName("height")]
    public int Height { get; set; }
}

The [JsonPropertyName] attributes handle the mapping between Strapi's camelCase JSON keys and your C# properties. JsonSerializerDefaults.Web provides case-insensitive matching as a fallback, but explicit attributes keep things predictable.

Step 7: Build a Typed HttpClient Service

Typed clients are one of several IHttpClientFactory consumption patterns, and the best choice depends on the app's requirements. They provide IntelliSense, a single configuration location, and full dependency injection support.

// Services/IStrapiContentService.cs
using StrapiDotnetApp.Models;

namespace StrapiDotnetApp.Services;

public interface IStrapiContentService
{
    Task<StrapiListResponse<Article>?> GetArticlesAsync(
        int page = 1, CancellationToken ct = default);
    Task<StrapiSingleResponse<Article>?> GetArticleByDocumentIdAsync(
        string documentId, CancellationToken ct = default);
}
// Services/StrapiContentService.cs
using System.Net.Http.Json;
using System.Text.Json;
using StrapiDotnetApp.Models;

namespace StrapiDotnetApp.Services;

public class StrapiContentService : IStrapiContentService
{
    private readonly HttpClient _httpClient;
    private readonly ILogger<StrapiContentService> _logger;
    private readonly string _baseUrl;
    private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web);

    public StrapiContentService(
        HttpClient httpClient,
        ILogger<StrapiContentService> logger,
        IConfiguration configuration)
    {
        _httpClient = httpClient;
        _logger = logger;
        _baseUrl = configuration["Strapi:BaseUrl"] ?? "http://localhost:1337";
    }

public async Task<StrapiListResponse<Article>?> GetArticlesAsync(
        int page = 1, CancellationToken ct = default)
    {
        try
        {
            var result = await _httpClient.GetFromJsonAsync<StrapiListResponse<Article>>(
                $"/api/articles?pagination[page]={page}&pagination[pageSize]=25&sort=createdAt:desc",
                JsonOptions, ct);

            if (result?.Data is not null)
                ResolveMediaUrls(result.Data);

            return result;
        }
        catch (HttpRequestException ex)
        {
            _logger.LogError(ex, "HTTP error fetching articles: {StatusCode}", ex.StatusCode);
            return null;
        }
    }

    public async Task<StrapiSingleResponse<Article>?> GetArticleByDocumentIdAsync(
        string documentId, CancellationToken ct = default)
    {
        try
        {
            var result = await _httpClient.GetFromJsonAsync<StrapiSingleResponse<Article>>(
                $"/api/articles/{documentId}?populate=cover",
                JsonOptions, ct);

            if (result?.Data?.Cover is not null)
                result.Data.Cover.Url = ResolveMediaUrl(result.Data.Cover.Url);

            return result;
        }
        catch (HttpRequestException ex)
        {
            _logger.LogError(ex, "HTTP error fetching article {DocumentId}: {StatusCode}",
                documentId, ex.StatusCode);
            return null;
        }
    }

    private void ResolveMediaUrls(List<Article> articles)
    {
        foreach (var article in articles)
        {
            if (article.Cover?.Url is not null)
                article.Cover.Url = ResolveMediaUrl(article.Cover.Url);
        }
    }

    private string ResolveMediaUrl(string url)
    {
        if (Uri.IsWellFormedUriString(url, UriKind.Absolute))
            return url;

        return $"{_baseUrl.TrimEnd('/')}{url}";
    }
}

A few things worth noting here. The populate=cover query parameter is required because Strapi's REST API does not populate relations by default. Without it, the cover field is not returned in the API response. The ResolveMediaUrl helper prepends the Strapi base URL to relative paths from the local upload provider—if you're using a cloud provider like AWS S3 or Cloudinary, the URL is already absolute and the helper passes it through unchanged.

Step 8: Register Services and Configure Program.cs

Add Strapi connection settings to appsettings.json:

{
  "Strapi": {
    "BaseUrl": "http://localhost:1337",
    "ApiToken": "your-api-token-here"
  }
}

Configure the typed client in Program.cs:

// Program.cs
using System.Net.Http.Headers;
using Microsoft.Extensions.Options;
using StrapiDotnetApp.Services;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllersWithViews();

// Typed HTTP client for Strapi
builder.Services.AddHttpClient<IStrapiContentService, StrapiContentService>((sp, client) =>
{
    var config = sp.GetRequiredService<IConfiguration>();
    client.BaseAddress = new Uri(config["Strapi:BaseUrl"]!);
    client.DefaultRequestHeaders.Authorization =
        new AuthenticationHeaderValue("Bearer", config["Strapi:ApiToken"]);
    client.DefaultRequestHeaders.Add("Accept", "application/json");
})
.SetHandlerLifetime(TimeSpan.FromMinutes(5))
.AddStandardResilienceHandler();

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");

app.Run();

The AddStandardResilienceHandler() call from Microsoft.Extensions.Http.Resilience adds a five-layer resilience pipeline—rate limiter, total timeout, retry, circuit breaker, and attempt timeout—in a single line. For any production integration against a content API, this should be standard configuration.

Using IHttpClientFactory instead of new HttpClient() helps avoid socket exhaustion and DNS staleness issues. The factory pools HttpMessageHandler instances and manages their lifecycle automatically.

Step 9: Build the Controller

The controller delegates entirely to the service layer—no direct HTTP calls or JSON parsing:

// Controllers/ArticlesController.cs
using Microsoft.AspNetCore.Mvc;
using StrapiDotnetApp.Services;

namespace StrapiDotnetApp.Controllers;

public class ArticlesController : Controller
{
    private readonly IStrapiContentService _strapiService;

    public ArticlesController(IStrapiContentService strapiService)
        => _strapiService = strapiService;

    public async Task<IActionResult> Index(int page = 1)
    {
        var result = await _strapiService.GetArticlesAsync(page);
        if (result is null) return View("Error");
        return View(result);
    }

    public async Task<IActionResult> Detail(string documentId)
    {
        var result = await _strapiService.GetArticleByDocumentIdAsync(documentId);
        if (result?.Data is null) return NotFound();
        return View(result.Data);
    }
}

Step 10: Create Razor Views

The listing view iterates over the Data collection and renders pagination from the Meta object:

@* Views/Articles/Index.cshtml *@
@using StrapiDotnetApp.Models
@model StrapiListResponse<Article>

@{
    ViewData["Title"] = "Articles";
}

<h1>Articles</h1>

@if (Model?.Data is not null && Model.Data.Any())
{
    <div class="row">
        @foreach (var article in Model.Data)
        {
            <div class="col-md-6 mb-4">
                <div class="card">
                    @if (article.Cover is not null)
                    {
                        <img src="@article.Cover.Url"
                             alt="@article.Cover.AlternativeText"
                             width="@article.Cover.Width"
                             height="@article.Cover.Height"
                             class="card-img-top"
                             loading="lazy" />
                    }
                    <div class="card-body">
                        <h2 class="card-title">
                            <a asp-action="Detail"
                               asp-route-documentId="@article.DocumentId">
                                @article.Title
                            </a>
                        </h2>
                        @if (article.PublishedAt.HasValue)
                        {
                            <time datetime="@article.PublishedAt.Value.ToString("yyyy-MM-dd")">
                                @article.PublishedAt.Value.ToString("MMMM d, yyyy")
                            </time>
                        }
                    </div>
                </div>
            </div>
        }
    </div>

    @if (Model.Meta?.Pagination is { } paging)
    {
        <nav>
            <p>Page @paging.Page of @paging.PageCount (@paging.Total total articles)</p>
            @if (paging.Page > 1)
            {
                <a asp-action="Index" asp-route-page="@(paging.Page - 1)">Previous</a>
            }
            @if (paging.Page < paging.PageCount)
            {
                <a asp-action="Index" asp-route-page="@(paging.Page + 1)">Next</a>
            }
        </nav>
    }
}
else
{
    <p>No articles available.</p>
}

The detail view renders a single article:

@* Views/Articles/Detail.cshtml *@
@using StrapiDotnetApp.Models
@model Article

@{
    ViewData["Title"] = Model.Title;
}

<article>
    <h1>@Model.Title</h1>

    @if (Model.PublishedAt.HasValue)
    {
        <time datetime="@Model.PublishedAt.Value.ToString("yyyy-MM-dd")">
            Published @Model.PublishedAt.Value.ToString("MMMM d, yyyy")
        </time>
    }

    @if (Model.Cover is not null)
    {
        <img src="@Model.Cover.Url"
             alt="@Model.Cover.AlternativeText"
             width="@Model.Cover.Width"
             height="@Model.Cover.Height" />
    }
</article>

Project Example: Knowledge Base with Article Search and Categories

This example extends the integration into a knowledge base application where articles are organized by categories, support filtered queries, and include a search feature. It demonstrates how Strapi APIs can be consumed by a .NET service layer.

Set Up the Category Content Type

In Strapi's Content-Type Builder, create a Category Collection Type:

  1. Click + next to Collection types, enter Category
  2. Add a Text field named name (Required, Unique)
  3. Add a Relation field—select many-to-many with Article
  4. Click Save

Create a few categories (e.g., "Getting Started", "Advanced", "Deployment"), assign them to your articles, and publish everything. Set Public role permissions to allow find and findOne on both Article and Category.

Extend the C# Models

// Models/Category.cs
using System.Text.Json.Serialization;

namespace StrapiDotnetApp.Models;

public class Category
{
    [JsonPropertyName("id")]
    public int Id { get; set; }

    [JsonPropertyName("documentId")]
    public string DocumentId { get; set; } = string.Empty;

    [JsonPropertyName("name")]
    public string? Name { get; set; }
}

Update the Article model to include categories:

// Add to Article.cs
[JsonPropertyName("categories")]
public List&lt;Category&gt;? Categories { get; set; }

Extend the Service Interface and Implementation

// Add to IStrapiContentService.cs
Task&lt;StrapiListResponse&lt;Article&gt;?> SearchArticlesAsync(
    string? query = null, string? categorySlug = null,
    int page = 1, CancellationToken ct = default);

Task&lt;StrapiListResponse&lt;Category&gt;?> GetCategoriesAsync(CancellationToken ct = default);
// Add to StrapiContentService.cs
public async Task&lt;StrapiListResponse&lt;Article&gt;?> SearchArticlesAsync(
    string? query = null, string? categorySlug = null,
    int page = 1, CancellationToken ct = default)
{
    try
    {
        var url = $"/api/articles?pagination[page]={page}&pagination[pageSize]=25"
                + "&sort=createdAt:desc&populate=cover&populate=categories";

        if (!string.IsNullOrWhiteSpace(query))
            url += $"&filters[title][$containsi]={Uri.EscapeDataString(query)}";

        if (!string.IsNullOrWhiteSpace(categorySlug))
            url += $"&filters[categories][name][$eqi]={Uri.EscapeDataString(categorySlug)}";

        var result = await _httpClient.GetFromJsonAsync<StrapiListResponse<Article>>(
            url, JsonOptions, ct);

        if (result?.Data is not null)
            ResolveMediaUrls(result.Data);

        return result;
    }
    catch (HttpRequestException ex)
    {
        _logger.LogError(ex, "HTTP error searching articles: {StatusCode}", ex.StatusCode);
        return null;
    }
}

public async Task&lt;StrapiListResponse&lt;Category&gt;?> GetCategoriesAsync(
    CancellationToken ct = default)
{
    try
    {
        return await _httpClient.GetFromJsonAsync<StrapiListResponse<Category>>(
            "/api/categories?sort=name:asc", JsonOptions, ct);
    }
    catch (HttpRequestException ex)
    {
        _logger.LogError(ex, "HTTP error fetching categories: {StatusCode}", ex.StatusCode);
        return null;
    }
}

The filter $containsi performs a case-insensitive substring match on the title field. Filtering on categories[name] is a deep filter—it traverses the relation and matches against the category's name field. Both use Strapi's LHS bracket syntax for query parameters.

Build the Knowledge Base Controller

// Controllers/KnowledgeBaseController.cs
using Microsoft.AspNetCore.Mvc;
using StrapiDotnetApp.Models;
using StrapiDotnetApp.Services;

namespace StrapiDotnetApp.Controllers;

public class KnowledgeBaseController : Controller
{
    private readonly IStrapiContentService _strapiService;

    public KnowledgeBaseController(IStrapiContentService strapiService)
        => _strapiService = strapiService;

    public async Task<IActionResult> Index(
        string? q = null, string? category = null, int page = 1)
    {
        var categoriesTask = _strapiService.GetCategoriesAsync();
        var articlesTask = _strapiService.SearchArticlesAsync(q, category, page);

        await Task.WhenAll(categoriesTask, articlesTask);

        ViewBag.Categories = categoriesTask.Result?.Data ?? new List<Category>();
        ViewBag.CurrentQuery = q;
        ViewBag.CurrentCategory = category;

        return View(articlesTask.Result);
    }
}

Both API calls execute concurrently with Task.WhenAll, cutting the page load time roughly in half compared to sequential requests. This is one area where async patterns in ASP.NET Core really pay off.

Create the Knowledge Base View

@* Views/KnowledgeBase/Index.cshtml *@
@using StrapiDotnetApp.Models
@model StrapiListResponse<Article>

@{
    ViewData["Title"] = "Knowledge Base";
    var categories = ViewBag.Categories as List<Category> ?? new();
    var currentQuery = ViewBag.CurrentQuery as string;
    var currentCategory = ViewBag.CurrentCategory as string;
}

<h1>Knowledge Base</h1>

<form method="get" asp-action="Index" class="mb-4">
    <div class="row g-2">
        <div class="col-md-6">
            <input type="text" name="q" value="@currentQuery"
                   placeholder="Search articles..." class="form-control" />
        </div>
        <div class="col-md-4">
            <select name="category" class="form-select">
                <option value="">All categories</option>
                @foreach (var cat in categories)
                {
                    <option value="@cat.Name"
                            selected="@(cat.Name == currentCategory)">
                        @cat.Name
                    </option>
                }
            </select>
        </div>
        <div class="col-md-2">
            <button type="submit" class="btn btn-primary w-100">Search</button>
        </div>
    </div>
</form>

@if (Model?.Data is not null && Model.Data.Any())
{
    @foreach (var article in Model.Data)
    {
        <article class="card mb-3">
            <div class="card-body">
                <h2 class="card-title">
                    <a asp-controller="Articles" asp-action="Detail"
                       asp-route-documentId="@article.DocumentId">
                        @article.Title
                    </a>
                </h2>
                @if (article.Categories?.Any() == true)
                {
                    <div>
                        @foreach (var cat in article.Categories)
                        {
                            <span class="badge bg-secondary me-1">@cat.Name</span>
                        }
                    </div>
                }
            </div>
        </article>
    }
}
else
{
    <p>No articles found.</p>
}

Strapi Open Office Hours

If you have any questions about Strapi 5 or just would like to stop by and say hi, you can join us at Strapi's Discord Open Office Hours, Monday through Friday, at 6:00 am CST and 12:30 pm CST: Strapi Discord Open Office Hours.

For more details, visit the Strapi documentation and .NET documentation.

FAQ

Can I use Strapi's GraphQL API instead of REST with .NET?

Yes. Install the GraphQL plugin with npm install @strapi/plugin-graphql in your Strapi project. The endpoint is available at /graphql and accepts standard HTTP POST requests with query and variables fields. Any .NET GraphQL client library, such as the GraphQL.Client NuGet package, can query this endpoint using the same Authorization: Bearer <token> header. Strapi auto-generates the GraphQL schema from your content types when shadowCRUD is enabled (the default).

How do I handle Strapi's Blocks rich text format in ASP.NET Core Razor views?

Strapi v5's Blocks rich text format returns structured JSON with typed nodes like paragraph, heading, and list—not raw HTML. Create C# model classes for StrapiBlock and StrapiBlockChild that map each node's type, children, text, and formatting properties. Then build a Razor partial view or tag helper that switches on the block type and renders the corresponding HTML elements. This approach gives you full control over output markup and styling.

Does the Strapi v5 response format differ from v4 for .NET model classes?

Significantly. Strapi v5 uses a flattened response format where fields sit directly on the data object. The v4 pattern of data.attributes.fieldName is removed. Your C# classes should map title to data.attributes.title, not data.title. Additionally, content is identified by an alphanumeric documentId string rather than an integer id. The publicationState parameter is replaced with status, accepting draft or published values.

How should I deploy Strapi and my .NET application together in production?

The recommended approach is separate containers in a shared Docker Compose network. Strapi runs as one service, your .NET app as another, and they communicate over the internal Docker network using service names as hostnames (e.g., http://strapi:1337). Set connection.pool.min to 0 in Strapi's database configuration for Docker environments, use environment variables for all secrets, and build the Strapi admin panel with NODE_ENV=production yarn build before starting.

Can I auto-generate C# client code from Strapi's API instead of writing models manually?

Strapi v5 includes an OpenAPI specification package that programmatically generates a spec describing all your content APIs. Feed this spec into NSwag or a similar .NET code generation tool to produce typed C# client classes automatically. This eliminates manual model creation for every content type and keeps your .NET client in sync with schema changes made in Strapi's Content-Type Builder. Regenerate the client whenever content types change.

Frequently Asked Questions

Integrating .NET with Strapi enhances the flexibility of your application by combining Strapi's content management capabilities with .NET's backend power. It enables fast, API-driven development, simplifies content management, and supports the creation of dynamic, scalable applications. This integration benefits both frontend and backend developers by allowing them to work independently while ensuring seamless communication between systems.

Yes, .NET can be used with Strapi to build dynamic websites. Strapi handles content management, while .NET handles business logic, transactions, and user authentication. The integration of both allows you to create dynamic, data-driven websites with a flexible backend powered by Strapi and a robust frontend built with .NET.

To integrate .NET with Strapi, you need the .NET SDK, Node.js, Strapi (v4 or newer), PostgreSQL (for production), and familiarity with RESTful APIs or GraphQL. A basic understanding of JavaScript and API integration is recommended.

Strapi automatically creates RESTful API endpoints for content. In .NET, you can create a client to interact with these APIs, sending HTTP requests to Strapi and processing the data. Authentication is handled through JWT, and you can manage content fetching, creation, updates, and deletion using simple API calls.

For secure communication, use JWT authentication between Strapi and .NET. Implement token management to refresh and validate tokens automatically. Ensure sensitive credentials like API keys and JWT tokens are stored securely in environment variables and use HTTPS for all API communications.