SuccessHound 1.0.0
See the version list below for details.
dotnet add package SuccessHound --version 1.0.0
NuGet\Install-Package SuccessHound -Version 1.0.0
<PackageReference Include="SuccessHound" Version="1.0.0" />
<PackageVersion Include="SuccessHound" Version="1.0.0" />
<PackageReference Include="SuccessHound" />
paket add SuccessHound --version 1.0.0
#r "nuget: SuccessHound, 1.0.0"
#:package SuccessHound@1.0.0
#addin nuget:?package=SuccessHound&version=1.0.0
#tool nuget:?package=SuccessHound&version=1.0.0
SuccessHound
<p align="center"> <img src="assets/logo.png" alt="SuccessHound Logo" style="width: 180px; border-radius: 20px;"/> </p>
<p align="center"> <em>A lightweight, extensible .NET library for wrapping API responses in consistent success envelopes.</em> </p>
<p align="center"> <a href="https://www.nuget.org/packages/SuccessHound"><img src="https://img.shields.io/nuget/v/SuccessHound?style=flat-square&logo=nuget&label=SuccessHound" alt="NuGet Version"></a> <a href="https://www.nuget.org/packages/SuccessHound.AspNetExtensions"><img src="https://img.shields.io/nuget/v/SuccessHound.AspNetExtensions?style=flat-square&logo=nuget&label=AspNetExtensions" alt="NuGet Version"></a> <a href="https://www.nuget.org/packages/SuccessHound.Pagination"><img src="https://img.shields.io/nuget/v/SuccessHound.Pagination?style=flat-square&logo=nuget&label=Pagination" alt="NuGet Version"></a> </p>
<p align="center"> <a href="https://www.nuget.org/packages/SuccessHound"><img src="https://img.shields.io/nuget/dt/SuccessHound?style=flat-square&logo=nuget&label=downloads&color=blue" alt="NuGet Downloads"></a> <a href="https://github.com/CydoEntis/SuccessHound/blob/master/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green?style=flat-square" alt="License"></a> <a href="https://dotnet.microsoft.com/download"><img src="https://img.shields.io/badge/.NET-8.0-purple?style=flat-square&logo=dotnet" alt=".NET Version"></a> <a href="https://github.com/CydoEntis/SuccessHound"><img src="https://img.shields.io/github/stars/CydoEntis/SuccessHound?style=flat-square&logo=github" alt="GitHub Stars"></a> </p>
Why SuccessHound?
- Consistent API responses - Standardized success envelope across your entire API
- Type-safe - Full generic support with IntelliSense
- Extensible - Factory pattern lets you customize everything
- Optional features - Only install what you need (core + pagination)
- Framework-agnostic - Core works anywhere, extensions are optional
- Zero ceremony - Fluent API with minimal configuration
Packages
Installation
Basic Setup (No Pagination)
dotnet add package SuccessHound
dotnet add package SuccessHound.AspNetExtensions
With Pagination
dotnet add package SuccessHound
dotnet add package SuccessHound.AspNetExtensions
dotnet add package SuccessHound.Pagination
Quick Start
Minimal Setup
using SuccessHound.Extensions;
using SuccessHound.Defaults;
var builder = WebApplication.CreateBuilder(args);
// Configure SuccessHound with DI
builder.Services.AddSuccessHound(options =>
{
options.UseFormatter<DefaultSuccessFormatter>();
});
var app = builder.Build();
// Optional: Add middleware (currently pass-through)
app.UseSuccessHound();
app.MapGet("/users/{id}", (int id, HttpContext context) =>
{
var user = new { Id = id, Name = "John Doe", Email = "john@example.com" };
return user.Ok(context); // Wraps in success envelope
});
app.Run();
Response:
{
"success": true,
"data": {
"id": 1,
"name": "John Doe",
"email": "john@example.com"
},
"meta": null,
"timestamp": "2025-12-15T10:30:00.000Z"
}
With Pagination
using SuccessHound.Extensions;
using SuccessHound.Defaults;
using SuccessHound.Pagination;
using SuccessHound.Pagination.Extensions;
var builder = WebApplication.CreateBuilder(args);
// Configure SuccessHound with pagination
builder.Services.AddSuccessHound(options =>
{
options.UseFormatter<DefaultSuccessFormatter>();
options.UsePagination(); // Add this line!
});
var app = builder.Build();
app.UseSuccessHound();
app.MapGet("/users", async (AppDbContext db, HttpContext context, int page = 1, int pageSize = 10) =>
{
return await db.Users
.OrderBy(u => u.Id)
.ToPagedResultAsync(page, pageSize, context);
});
app.Run();
Response:
{
"success": true,
"data": [
{
"id": 1,
"name": "Alice"
},
{
"id": 2,
"name": "Bob"
}
],
"meta": {
"pagination": {
"page": 1,
"pageSize": 10,
"totalCount": 100,
"totalPages": 10,
"hasNextPage": true,
"hasPreviousPage": false
}
},
"timestamp": "2025-12-15T10:30:00.000Z"
}
Response Structure
All responses wrapped by SuccessHound include:
public class ApiResponse<T>
{
public bool Success { get; init; } // Always true for success responses
public T? Data { get; init; } // Your payload
public object? Meta { get; init; } // Optional metadata (pagination, etc.)
public DateTime Timestamp { get; init; } // UTC timestamp
}
Extension Methods
.Ok<T>()
Returns 200 OK with wrapped data.
app.MapGet("/products/{id}", (int id, HttpContext context) =>
{
var product = GetProduct(id);
return product.Ok(context);
});
Response: 200 OK
{
"success": true,
"data": {
"id": 1,
"name": "Product Name"
},
"meta": null,
"timestamp": "2025-12-15T10:30:00.000Z"
}
.Created<T>(string location)
Returns 201 Created with Location header.
app.MapPost("/products", (Product product, HttpContext context) =>
{
var created = CreateProduct(product);
return created.Created($"/products/{created.Id}", context);
});
Response: 201 Created with Location: /products/123 header
.Updated<T>()
Returns 200 OK for update operations.
app.MapPut("/products/{id}", (int id, Product product, HttpContext context) =>
{
var updated = UpdateProduct(id, product);
return updated.Updated(context);
});
Response: 200 OK with wrapped data
.NoContent() / .Deleted()
Returns 204 No Content for delete operations.
app.MapDelete("/products/{id}", (int id) =>
{
DeleteProduct(id);
return SuccessHoundResultsExtensions.Deleted();
});
Response: 204 No Content (no body)
.WithMeta<T>(object meta)
Returns 200 OK with custom metadata.
app.MapGet("/products", (HttpContext context, int page = 1) =>
{
var products = GetProducts(page);
var meta = new
{
Page = page,
Version = "v1.0",
ServerTime = DateTime.UtcNow
};
return products.WithMeta(meta, context);
});
Response:
{
"success": true,
"data": [
...
],
"meta": {
"page": 1,
"version": "v1.0",
"serverTime": "2025-12-15T10:30:00.000Z"
},
"timestamp": "2025-12-15T10:30:00.000Z"
}
.Custom<T>(int statusCode)
Returns custom HTTP status code.
app.MapPost("/products/process", (Product product, HttpContext context) =>
{
var result = ProcessProduct(product);
return result.Custom(202, context); // 202 Accepted
});
Response: 202 Accepted with wrapped data
Pagination
EF Core Pagination
using SuccessHound.Pagination.Extensions;
app.MapGet("/users", async (AppDbContext db, HttpContext context, int page = 1, int pageSize = 20) =>
{
return await db.Users
.Where(u => u.IsActive)
.OrderBy(u => u.CreatedAt)
.ToPagedResultAsync(page, pageSize, context);
});
In-Memory Pagination
app.MapGet("/items", (HttpContext context, int page = 1, int pageSize = 10) =>
{
var items = GetAllItems(); // Returns IEnumerable<T>
return items.ToPagedResult(page, pageSize, context);
});
Pagination Metadata
Default pagination metadata includes:
{
"pagination": {
"page": 1,
"pageSize": 10,
"totalCount": 100,
"totalPages": 10,
"hasNextPage": true,
"hasPreviousPage": false
}
}
Advanced Usage
Custom Response Formatter
Create your own response structure:
using SuccessHound.Abstractions;
public sealed class MyCustomFormatter : ISuccessResponseFormatter
{
public object Format(object? data, object? meta = null)
{
return new
{
Status = "success",
Result = data,
Metadata = meta,
Version = "v2.0",
Timestamp = DateTime.UtcNow
};
}
}
Use it:
builder.Services.AddSuccessHound(options =>
{
options.UseFormatter<MyCustomFormatter>();
});
Custom Pagination Factory
Customize pagination metadata:
using SuccessHound.Pagination.Abstractions;
public sealed class MyPaginationFactory : IPaginationMetadataFactory
{
public object CreateMetadata(int page, int pageSize, int totalCount)
{
var totalPages = (int)Math.Ceiling(totalCount / (double)pageSize);
return new
{
CurrentPage = page,
PerPage = pageSize,
Total = totalCount,
TotalPages = totalPages,
Links = new
{
Next = page < totalPages ? $"/api?page={page + 1}" : null,
Previous = page > 1 ? $"/api?page={page - 1}" : null
}
};
}
}
Use it:
builder.Services.AddSuccessHound(options =>
{
options.UseFormatter<DefaultSuccessFormatter>();
options.UsePagination(new MyPaginationFactory());
});
Framework-Agnostic Usage
Use SuccessHound formatters outside of ASP.NET Core:
using SuccessHound.Abstractions;
using SuccessHound.Defaults;
// Create formatter
var formatter = new DefaultSuccessFormatter();
// Format data
var data = new { Message = "Hello, World!" };
var wrapped = formatter.Format(data);
// With metadata
var meta = new { Version = "1.0" };
var wrappedWithMeta = formatter.Format(data, meta);
Common Scenarios
Handling Null Data
SuccessHound handles null data gracefully:
app.MapGet("/user/{id}", (int id, HttpContext context) =>
{
var user = FindUser(id); // May return null
return user.Ok(context);
});
Response:
{
"success": true,
"data": null,
"meta": null,
"timestamp": "2025-12-15T10:30:00.000Z"
}
Collections
app.MapGet("/users", (HttpContext context) =>
{
var users = new List<User>
{
new User { Id = 1, Name = "Alice" },
new User { Id = 2, Name = "Bob" }
};
return users.Ok(context);
});
Response:
{
"success": true,
"data": [
{
"id": 1,
"name": "Alice"
},
{
"id": 2,
"name": "Bob"
}
],
"meta": null,
"timestamp": "2025-12-15T10:30:00.000Z"
}
Complex Metadata
app.MapGet("/report", (HttpContext context) =>
{
var report = GenerateReport();
var meta = new
{
GeneratedAt = DateTime.UtcNow,
GeneratedBy = "System",
Format = "JSON",
Version = "1.0",
Filters = new { StartDate = "2025-01-01", EndDate = "2025-12-31" }
};
return report.WithMeta(meta, context);
});
Requirements
- .NET 8.0 or higher
- ASP.NET Core 8.0+ (for AspNetExtensions package)
- Entity Framework Core 8.0+ (for
ToPagedResultAsync()in Pagination package)
License
MIT License - See LICENSE for details.
Built with care for clean, consistent API responses.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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 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. |
-
net8.0
NuGet packages (3)
Showing the top 3 NuGet packages that depend on SuccessHound:
| Package | Downloads |
|---|---|
|
Pawthorize
Modern authentication library for ASP.NET Core - simple, secure, and opinionated. Email-based authentication with JWT tokens, OAuth 2.0 social login (Google, Discord) with structured name support, refresh token rotation, CSRF protection, password reset, email verification, and enhanced session management with device tracking. Enforces best practices with separated FirstName/LastName fields. |
|
|
SuccessHound.AspNetExtensions
ASP.NET Core Minimal API extensions for SuccessHound. Provides fluent extension methods (.Ok(), .Created(), .Updated(), etc.) for wrapping responses. |
|
|
SuccessHound.Pagination
Optional pagination package for SuccessHound. Provides EF Core and in-memory pagination with customizable metadata factories. Includes ToPagedResultAsync() extension methods. |
GitHub repositories
This package is not used by any popular GitHub repositories.
v1.0.0 - Major refactor: DI-first architecture with no static state. Breaking changes: Moved from static SuccessHound.Configure() to builder.Services.AddSuccessHound(). See README for migration guide.