SwiftStack 0.4.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package SwiftStack --version 0.4.0
                    
NuGet\Install-Package SwiftStack -Version 0.4.0
                    
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="SwiftStack" Version="0.4.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="SwiftStack" Version="0.4.0" />
                    
Directory.Packages.props
<PackageReference Include="SwiftStack" />
                    
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 SwiftStack --version 0.4.0
                    
#r "nuget: SwiftStack, 0.4.0"
                    
#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 SwiftStack@0.4.0
                    
#: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=SwiftStack&version=0.4.0
                    
Install as a Cake Addin
#tool nuget:?package=SwiftStack&version=0.4.0
                    
Install as a Cake Tool

alt tag

SwiftStack

NuGet Version NuGet

SwiftStack is an opinionated and easy way to build distributed systems — RESTful, message queue–oriented, or WebSocket–based — inspired by the elegant model shown in FastAPI (Python) but designed for C# developers who value clarity and speed.

MIT Licensed • No ceremony • Just build.


✨ New in v0.4.x

  • OpenAPI 3.0 and Swagger UI support for REST APIs

🚀 Simple REST Example

<details> <summary>Click to expand</summary>

using SwiftStack;

class Program
{
    static async Task Main(string[] args)
    {
        SwiftStackApp app = new SwiftStackApp("My test application");

        app.Rest.Route("GET", "/", async (req) => "Hello world");

        app.Rest.Route("POST", "/loopback", async (req) => req.Data);

        app.Rest.Get("/search", async (req) =>
        {
            string query = req.Query["q"];
            if (string.IsNullOrEmpty(query)) query = "no query provided";
            int page = int.TryParse(req.Query["page"] as string, out int p) ? p : 1;

            return new
            {
                Query = query,
                Page = page,
                Message = $"Searching for '{query}' on page {page}"
            };
        });

        await app.Rest.Run();
    }
}

</details>


🔐 REST Example with Authentication

<details> <summary>Click to expand</summary>

using SwiftStack;

class Program
{
    static async Task Main(string[] args)
    {
        SwiftStackApp app = new SwiftStackApp("My secure app");
        app.Rest.AuthenticationRoute = AuthenticationRoute;

        app.Rest.Route("GET", "/authenticated", async (req) => "Hello, authenticated user", true);

        await app.Rest.Run();
    }

    static async Task<AuthResult> AuthenticationRoute(HttpContextBase ctx)
    {

        if (ctx.Request.Authorization?.Username == "user" &&
            ctx.Request.Authorization?.Password == "password")
        {
            ctx.Metadata = new { Authorized = true };                    
            return new AuthResult
            {
                AuthenticationResult = AuthenticationResultEnum.Success,
                AuthorizationResult = AuthorizationResultEnum.Permitted
            };
        }
        else
        {
            return new AuthResult
            {
                AuthenticationResult = AuthenticationResultEnum.NotFound,
                AuthorizationResult = AuthorizationResultEnum.DeniedImplicit
            };
        }
    }
}

</details>


📖 REST with OpenAPI and Swagger UI

<details> <summary>Click to expand</summary>

SwiftStack includes built-in OpenAPI 3.0 documentation and Swagger UI for your REST APIs.

Basic Setup

using SwiftStack;
using SwiftStack.Rest.OpenApi;

SwiftStackApp app = new SwiftStackApp("My API");

// Enable OpenAPI with configuration
app.Rest.UseOpenApi(openApi =>
{
    openApi.Info.Title = "My API";
    openApi.Info.Version = "1.0.0";
    openApi.Info.Description = "A sample API built with SwiftStack";
    openApi.Info.Contact = new OpenApiContact("Support", "support@example.com");

    // Define tags for grouping endpoints
    openApi.Tags.Add(new OpenApiTag("Users", "User management endpoints"));
    openApi.Tags.Add(new OpenApiTag("Products", "Product catalog endpoints"));

    // Define security schemes
    openApi.SecuritySchemes["Bearer"] = OpenApiSecurityScheme.Bearer(
        "JWT",
        "Enter your JWT token");
    openApi.SecuritySchemes["Basic"] = OpenApiSecurityScheme.Basic(
        "Use username:password");
});

await app.Rest.Run();

This automatically creates:

  • OpenAPI JSON at /openapi.json
  • Swagger UI at /swagger

Documenting Routes

Use the fluent API to add metadata to your routes:

// Simple route with documentation
app.Rest.Get("/", async (req) => "Hello, World!",
    api => api
        .WithTag("General")
        .WithSummary("Root endpoint")
        .WithDescription("Returns a simple greeting message"));

// Route with path parameters
app.Rest.Get("/users/{id}", async (req) =>
{
    string id = req.Parameters["id"];
    return new { Id = id, Name = "John Doe" };
},
api => api
    .WithTag("Users")
    .WithSummary("Get user by ID")
    .WithParameter(OpenApiParameterMetadata.Path("id", "The user ID"))
    .WithResponse(200, OpenApiResponseMetadata.Json<User>("User details"))
    .WithResponse(404, OpenApiResponseMetadata.NotFound()));

// Route with query parameters
app.Rest.Get("/search", async (req) =>
{
    string query = req.Query["q"];
    int page = int.TryParse(req.Query["page"] as string, out int p) ? p : 1;
    return new { Query = query, Page = page };
},
api => api
    .WithTag("General")
    .WithSummary("Search endpoint")
    .WithParameter(OpenApiParameterMetadata.Query("q", "Search query", true))
    .WithParameter(OpenApiParameterMetadata.Query("page", "Page number", false,
        OpenApiSchemaMetadata.Integer())));

// Route with request body (type is inferred from generic parameter)
app.Rest.Post<User>("/users", async (req) =>
{
    User user = req.GetData<User>();
    return new { Id = Guid.NewGuid(), user.Email };
},
api => api
    .WithTag("Users")
    .WithSummary("Create a new user")
    .WithRequestBody(OpenApiRequestBodyMetadata.Json<User>("User to create", true))
    .WithResponse(201, OpenApiResponseMetadata.Json<User>("Created user")));

// Authenticated route
app.Rest.Get("/profile", async (req) => new { Email = "user@example.com" },
    api => api
        .WithTag("Users")
        .WithSummary("Get current user profile")
        .WithSecurity("Bearer")
        .WithResponse(200, OpenApiResponseMetadata.Json<User>("User profile"))
        .WithResponse(401, OpenApiResponseMetadata.Unauthorized()),
    requireAuthentication: true);

Schema Generation

Schemas are automatically generated from your C# types:

public class User
{
    public string Id { get; set; }
    public string Email { get; set; }
    public string Name { get; set; }
    public DateTime CreatedAt { get; set; }
}

// Use in responses - schema is auto-generated via reflection
.WithResponse(200, OpenApiResponseMetadata.Json<User>("User data"))

// Or create schemas manually
OpenApiSchemaMetadata.String()                    // string
OpenApiSchemaMetadata.Integer()                   // int32
OpenApiSchemaMetadata.Long()                      // int64
OpenApiSchemaMetadata.Number()                    // double
OpenApiSchemaMetadata.Boolean()                   // boolean
OpenApiSchemaMetadata.Array(OpenApiSchemaMetadata.String())  // string[]
OpenApiSchemaMetadata.FromType<MyClass>()         // complex object

Common Response Helpers

OpenApiResponseMetadata.Json<T>(description)      // 200 with JSON body
OpenApiResponseMetadata.Text(description)         // 200 with text body
OpenApiResponseMetadata.NoContent()               // 204 No Content
OpenApiResponseMetadata.BadRequest()              // 400 Bad Request
OpenApiResponseMetadata.Unauthorized()            // 401 Unauthorized
OpenApiResponseMetadata.NotFound()                // 404 Not Found
OpenApiResponseMetadata.Conflict()                // 409 Conflict
OpenApiResponseMetadata.InternalServerError()     // 500 Internal Server Error

Configuration Options

app.Rest.UseOpenApi(openApi =>
{
    // Customize paths (defaults shown)
    openApi.DocumentPath = "/openapi.json";
    openApi.SwaggerUiPath = "/swagger";
    openApi.EnableSwaggerUi = true;

    // API info
    openApi.Info.Title = "My API";
    openApi.Info.Version = "1.0.0";
    openApi.Info.Description = "API description";
    openApi.Info.TermsOfService = "https://example.com/terms";
    openApi.Info.Contact = new OpenApiContact("Name", "email@example.com");
    openApi.Info.License = new OpenApiLicense("MIT", "https://opensource.org/licenses/MIT");

    // External docs
    openApi.ExternalDocs = new OpenApiExternalDocs(
        "https://docs.example.com",
        "Full documentation");

    // Server configuration
    openApi.Servers.Add(new OpenApiServer("https://api.example.com", "Production"));
    openApi.Servers.Add(new OpenApiServer("https://staging-api.example.com", "Staging"));
});

</details>


📨 Simple RabbitMQ Example

<details> <summary>Click to expand</summary>

SwiftStack includes first-class RabbitMQ support, including resilient producer/consumer and broadcaster/receiver patterns.
Resilient modes use on-disk index files to recover state across process restarts.

using SwiftStack;
using SwiftStack.RabbitMq;

// Initialize app and RabbitMQ integration
SwiftStackApp app = new SwiftStackApp("RabbitMQ Example");
RabbitMqApp rabbit = new RabbitMqApp(app);

// Define queue settings
QueueProperties queueProps = new QueueProperties
{
    Hostname = "localhost",
    Name = "demo-queue",
    AutoDelete = true
};

// Create producer and consumer
var producer = new RabbitMqProducer<string>(app.Logging, queueProps, 1024 * 1024);
var consumer = new RabbitMqConsumer<string>(app.Logging, queueProps, true);

consumer.MessageReceived += (sender, e) =>
{
    Console.WriteLine($"[Consumer] {e.Data}");
};

// Initialize and send
await producer.InitializeAsync();
await consumer.InitializeAsync();

for (int i = 1; i <= 5; i++)
{
    await producer.SendMessage($"Message {i}", Guid.NewGuid().ToString());
    await Task.Delay(500);
}

Resilient versions are identical except you use:

var producer = new ResilientRabbitMqProducer<string>(app.Logging, queueProps, "./producer.idx", 1024 * 1024);
var consumer = new ResilientRabbitMqConsumer<string>(app.Logging, queueProps, "./consumer.idx", 4, true);

and the same for broadcaster/receiver via:

var broadcaster = new RabbitMqBroadcaster<MyType>(...);
var receiver = new RabbitMqBroadcastReceiver<MyType>(...);

</details>


🔌Simple WebSockets Example

<details> <summary>Click to expand</summary>

SwiftStack makes it trivial to stand up WebSocket servers with routing, default handlers, and direct server→client messaging.

using SwiftStack;
using SwiftStack.Websockets;

SwiftStackApp app = new SwiftStackApp("WebSockets Demo");
WebsocketsApp wsApp = new WebsocketsApp(app);

// Route for "echo"
wsApp.AddRoute("echo", async (msg, token) =>
{
    await msg.RespondAsync($"Echo: {msg.DataAsString}");
});

// Default route
wsApp.DefaultRoute = async (msg, token) =>
{
    await msg.RespondAsync("No route matched, sorry!");
};

// Start server
app.LoggingSettings.EnableConsole = true;
Task serverTask = wsApp.Run("127.0.0.1", 9006, CancellationToken.None);

// Example: sending server→client message after connect
wsApp.ClientConnected += async (sender, client) =>
{
    await wsApp.WebsocketServer.SendAsync(client.Guid, "Welcome to the server!");
};

await serverTask;

Client (any WebSocket library works — here’s with System.Net.WebSockets):

using var ws = new ClientWebSocket();
await ws.ConnectAsync(new Uri("ws://127.0.0.1:9006/echo"), CancellationToken.None);
await ws.SendAsync(Encoding.UTF8.GetBytes("Hello"), WebSocketMessageType.Text, true, CancellationToken.None);

</details>


📦 Installation

dotnet add package SwiftStack

📜 Version History

See CHANGELOG.md for details.


❤️ Donations

If you’d like to financially support development: see DONATIONS.md.


Thanks to pngall.com for the lightning icon.


Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  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 is compatible.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 is compatible. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos 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
0.4.1 0 12/24/2025
0.4.0 81 12/21/2025
0.3.5 321 11/17/2025
0.3.4 275 11/17/2025
0.3.3 199 11/4/2025
0.3.2 244 10/8/2025
0.3.1 183 10/8/2025
0.3.0 170 8/15/2025
0.2.4 251 7/26/2025
0.2.3 277 7/26/2025
0.2.2 494 7/25/2025
0.2.1 576 7/23/2025
0.2.0 183 7/13/2025
0.1.20 283 5/14/2025
0.1.19 207 4/10/2025
0.1.18 163 4/4/2025
0.1.16 221 4/1/2025
0.1.15 203 4/1/2025
0.1.13 213 4/1/2025
0.1.12 205 3/30/2025
0.1.11 199 3/30/2025
0.1.10 198 3/30/2025
0.1.9 196 3/30/2025
0.1.8 131 3/29/2025
0.1.7 129 3/29/2025
0.1.5 149 3/29/2025
0.1.4 136 3/21/2025
0.1.3 195 3/12/2025
0.1.2 156 2/25/2025
0.1.1 146 2/25/2025
0.1.0 152 2/24/2025

Initial release