Aspire integration

A profanity filter, on tap, in your AppHost.

The ProfanityFilter.Hosting package wraps the Potty Mouth API as a first-class ContainerResource. Add it to your distributed application, reference it from any project, and you have a fast, local-first profanity-filter HTTP service alongside the rest of your services — no infra to spin up.

Aspire 13.3 C# AppHost TypeScript AppHost ghcr.io/ievangelist/profanity-filter-api
🐳

Containerized

Backed by the official ghcr.io/ievangelist/profanity-filter-api image. Same engine as the GitHub Action, served over HTTPS with a sensible default port.

🌐

Multi-language

Annotated with the new [AspireExport] attribute, so the same NuGet package powers both C# and TypeScript AppHosts.

🔌

Just works

Implements IResourceWithConnectionString. Reference the resource from any project and the HTTPS endpoint flows in automatically.

Install

From your AppHost directory, run the aspire add command. The CLI installs the NuGet package for C# AppHosts and additionally generates the typed .modules SDK for TypeScript AppHosts — same command, both languages.

terminal bash
aspire add ProfanityFilter.Hosting

C# AppHost

Call AddProfanityFilter on your IDistributedApplicationBuilder, then wire any project that needs to call the filter with .WithReference(...) and .WaitFor(...).

apphost.cs csharp
// apphost.cs
#:sdk Aspire.AppHost.Sdk@13.3.0
#:package Aspire.Hosting
#:package ProfanityFilter.Hosting
#:project ../src/MyApi/MyApi.csproj

var builder = DistributedApplication.CreateBuilder(args);

var profanityFilter = builder.AddProfanityFilter("profanity-filter")
    .WithCustomDataBindMount("./CustomData");

builder.AddProject<Projects.MyApi>("api")
    .WithReference(profanityFilter)
    .WaitFor(profanityFilter);

builder.Build().Run();

TypeScript AppHost

Thanks to Aspire’s multi-language authoring, the same package generates a typed TypeScript SDK at AppHost build time. The C# implementation runs as-is — the SDK is a thin client that calls into it over JSON-RPC.

apphost.ts ts
// apphost.ts
import { createBuilder } from "./.modules/aspire.js";
import { addProfanityFilter } from "./.modules/profanityfilter.hosting.js";

const builder = await createBuilder();

const filter = await addProfanityFilter(builder, "profanity-filter")
    .withCustomDataBindMount("./CustomData");

const api = await builder.addProject("api", { project: "../src/MyApi/MyApi.csproj" })
    .withReference(filter)
    .waitFor(filter);

const app = await builder.build();
await app.run();

Reference it from your code

The resource implements IResourceWithConnectionString, so referencing it from a project flows the HTTPS endpoint through as a connection string under the resource name. Pair that with ProfanityFilter.Client and a single AddProfanityFilterClient(...) call — it reads the connection string, registers a typed HttpClient, and exposes a strongly-typed ProfanityFilterClient with REST and SignalR-backed real-time clients ready to inject.

Program.cs csharp
// In your API project (MyApi.csproj) — install ProfanityFilter.Client, then:
builder.AddProfanityFilterClient("profanity-filter");

// Now inject ProfanityFilterClient anywhere — it's a typed
// HTTP client wired to the resource's connection string, with
// .Rest for REST calls and .Realtime for the SignalR hub.
app.MapPost("/scrub", async (
    string text,
    ProfanityFilterClient profanity) =>
{
    var response = await profanity.Rest.ApplyFilterAsync(
        new ProfanityFilterRequest(text, ReplacementStrategy.Emoji));

    return response.Match(
        onValue: r => Results.Ok(r.FilteredText),
        onAbsent: () => Results.NotFound());
});

Need keyed services? AddKeyedProfanityFilterClient("name") registers the same dependencies under a service key — useful when you reference more than one filter.

Custom word lists

Ship project-specific terms with WithCustomDataBindMount. Drop one or more newline-delimited *.txt files into the folder you point at — they’re merged with the built-in 4,900+ word dictionary at startup.

// Optional: ship a folder of newline-delimited *.txt files with
// project-specific words and phrases that should also be filtered.
// They are merged with the built-in 4,900+ word dictionary at runtime.

var profanityFilter = builder.AddProfanityFilter("profanity-filter")
    .WithCustomDataBindMount("./CustomData");

API surface

Member Description
AddProfanityFilter(name) Adds the profanity filter container to the distributed application. Returns an IResourceBuilder<ProfanityFilterResource>.
WithCustomDataBindMount(source) Bind-mounts a host folder of *.txt word lists into /app/CustomData inside the container.
ProfanityFilterResource.HttpsEndpoint The HTTPS endpoint reference. Implements IResourceWithConnectionString, so referencing the resource flows the URL through automatically.