Multi-Blog Setup (ASP.NET Core)

A single ASP.NET Core application can host multiple independent Postnomic blogs simultaneously. Each blog has its own API key, blog slug, content, and base path. This is useful when you want to serve ...

Overview

A single ASP.NET Core application can host multiple independent Postnomic blogs simultaneously. Each blog has its own API key, blog slug, content, and base path. This is useful when you want to serve a general company blog alongside a more technical engineering blog, or when you manage a product's public blog and an internal knowledge base from the same application.

Requests are automatically routed to the correct blog based on the URL path. The pages (Index, Post, Author) resolve the active blog at request time via IPostnomicBlogResolver, so no additional routing configuration is required beyond MapRazorPages().

Registration

The named overload of AddPostnomicBlog accepts a blog name as its first argument. Each registration must use a unique name and a distinct BasePath. The name is an arbitrary string that you define — it is used internally to identify the registration.

builder.Services.AddPostnomicBlog(options =>
{
    // Default (unnamed) registration — required as the fallback
    options.BaseUrl  = "https://api.postnomic.com";
    options.ApiKey   = "pk_company_blog_key";
    options.BlogSlug = "company-blog";
    options.BasePath = "/blog";
});

builder.Services.AddPostnomicBlog("engineering", options =>
{
    options.BaseUrl  = "https://api.postnomic.com";
    options.ApiKey   = "pk_engineering_blog_key";
    options.BlogSlug = "engineering-blog";
    options.BasePath = "/blog/engineering";
});

builder.Services.AddPostnomicBlog("updates", options =>
{
    options.BaseUrl  = "https://api.postnomic.com";
    options.ApiKey   = "pk_updates_blog_key";
    options.BlogSlug = "product-updates";
    options.BasePath = "/blog/updates";
});

The unnamed registration is still required. It acts as the default and handles any request that does not match a named registration's base path.

Branding is now server-enforced per blog. The API returns a showBranding value in each blog's info response, and the SDK uses that value automatically. You do not need to set ShowBranding in your client options — the server determines the correct value based on each blog owner's subscription tier.

ShowBranding on PostnomicClientOptions is a fallback that applies only before the blog info loads. If you want to set an explicit fallback for a free-tier blog you can still do so, but it is optional:

builder.Services.AddPostnomicBlog(options =>
{
    options.BaseUrl      = "https://api.postnomic.com";
    options.ApiKey       = "pk_company_blog_key";
    options.BlogSlug     = "company-blog";
    options.BasePath     = "/blog";
    options.ShowBranding = true; // Optional fallback — server value takes precedence
});

builder.Services.AddPostnomicBlog("engineering", options =>
{
    options.BaseUrl  = "https://api.postnomic.com";
    options.ApiKey   = "pk_engineering_blog_key";
    options.BlogSlug = "engineering-blog";
    options.BasePath = "/blog/engineering";
    // ShowBranding omitted — server handles it automatically
});

Complete Program.cs Example

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();

// Default blog at /blog
builder.Services.AddPostnomicBlog(options =>
{
    options.BaseUrl  = "https://api.postnomic.com";
    options.ApiKey   = builder.Configuration["Postnomic:CompanyBlog:ApiKey"]!;
    options.BlogSlug = "company-blog";
    options.BasePath = "/blog";
});

// Engineering blog at /blog/engineering
builder.Services.AddPostnomicBlog("engineering", options =>
{
    options.BaseUrl  = "https://api.postnomic.com";
    options.ApiKey   = builder.Configuration["Postnomic:EngineeringBlog:ApiKey"]!;
    options.BlogSlug = "engineering-blog";
    options.BasePath = "/blog/engineering";
});

// Product updates blog at /blog/updates
builder.Services.AddPostnomicBlog("updates", options =>
{
    options.BaseUrl  = "https://api.postnomic.com";
    options.ApiKey   = builder.Configuration["Postnomic:UpdatesBlog:ApiKey"]!;
    options.BlogSlug = "product-updates";
    options.BasePath = "/blog/updates";
});

var app = builder.Build();

app.UseStaticFiles();
app.UseRouting();
app.MapRazorPages();
app.Run();

No additional middleware or area registrations are needed. MapRazorPages() is sufficient.

Path-Based Resolution

When a request arrives at /blog/engineering/my-post-slug, the IPostnomicBlogResolver inspects the request path and finds the registration whose BasePath is the longest matching prefix. In this case, /blog/engineering matches before /blog, so the engineering blog's IPostnomicBlogService is used to fetch and render the post.

Resolution is prefix-based and greedy — a more specific path always wins over a less specific one. The ordering of AddPostnomicBlog calls in Program.cs does not affect resolution.

All links generated by the blog pages (post links, author links, sidebar navigation) are resolved relative to the active blog's BasePath. This means visitors browsing the engineering blog at /blog/engineering will stay within /blog/engineering/* when clicking on posts or author profiles — they will not accidentally navigate into the company blog at /blog/*.

This behaviour is automatic and requires no extra configuration. Each blog is fully self-contained from the visitor's perspective.

Using appsettings.json for Multiple Blogs

You can organise multi-blog configuration cleanly in appsettings.json using distinct sections:

{
  "Postnomic": {
    "CompanyBlog": {
      "BaseUrl": "https://api.postnomic.com",
      "ApiKey": "pk_company_blog_key",
      "BlogSlug": "company-blog",
      "BasePath": "/blog"
    },
    "EngineeringBlog": {
      "BaseUrl": "https://api.postnomic.com",
      "ApiKey": "pk_engineering_blog_key",
      "BlogSlug": "engineering-blog",
      "BasePath": "/blog/engineering"
    }
  }
}

Bind each section in Program.cs:

builder.Services.AddPostnomicBlog(
    builder.Configuration.GetSection("Postnomic:CompanyBlog")
);

builder.Services.AddPostnomicBlog(
    "engineering",
    builder.Configuration.GetSection("Postnomic:EngineeringBlog")
);

Backward Compatibility

Existing applications that use the single-blog AddPostnomicBlog(options) overload continue to work without any changes. The multi-blog feature is purely additive — adding named registrations does not affect the behaviour of the unnamed default registration.

Next Steps

  • See Multi-Blog Setup (Blazor) for the equivalent setup in Blazor applications
  • See API Key Authentication for guidance on managing separate keys per blog
  • See Caching to configure per-blog cache settings in a multi-blog deployment

Was this article helpful?

Thank you for your feedback!