Nabs.Launchpad.Core.Apis
10.0.217
Prefix Reserved
See the version list below for details.
dotnet add package Nabs.Launchpad.Core.Apis --version 10.0.217
NuGet\Install-Package Nabs.Launchpad.Core.Apis -Version 10.0.217
<PackageReference Include="Nabs.Launchpad.Core.Apis" Version="10.0.217" />
<PackageVersion Include="Nabs.Launchpad.Core.Apis" Version="10.0.217" />
<PackageReference Include="Nabs.Launchpad.Core.Apis" />
paket add Nabs.Launchpad.Core.Apis --version 10.0.217
#r "nuget: Nabs.Launchpad.Core.Apis, 10.0.217"
#:package Nabs.Launchpad.Core.Apis@10.0.217
#addin nuget:?package=Nabs.Launchpad.Core.Apis&version=10.0.217
#tool nuget:?package=Nabs.Launchpad.Core.Apis&version=10.0.217
Nabs Launchpad Core APIs Library
The Nabs Launchpad Core APIs library provides foundational components for building robust, production-ready Web APIs with ASP.NET Core. This library includes base controllers, result pattern integration, and standardized response handling for modern .NET applications.
Key Features
- Base Controller Classes: Pre-configured controller base classes following best practices
- Result Pattern Integration: Built-in support for Ardalis.Result pattern
- Standardized Responses: Consistent API response formatting
- ASP.NET Core Integration: Full compatibility with ASP.NET Core framework
- Minimal API Support: Works seamlessly with both controller-based and minimal APIs
- Error Handling: Structured error responses for better client experience
Core Components
NabsControllerBase
A base controller class that extends ControllerBase and provides a foundation for building API controllers with standardized patterns and response handling.
Usage Examples
Basic API Controller
using Microsoft.AspNetCore.Mvc;
using Nabs.Launchpad.Core.Apis;
using Ardalis.Result;
[ApiController]
[Route("api/[controller]")]
public class ProductsController : NabsControllerBase
{
private readonly IProductService _productService;
public ProductsController(IProductService productService)
{
_productService = productService;
}
[HttpGet]
public async Task<IActionResult> GetAll()
{
var products = await _productService.GetAllAsync();
return Ok(products);
}
[HttpGet("{id}")]
public async Task<IActionResult> GetById(Guid id)
{
var result = await _productService.GetByIdAsync(id);
if (result.IsSuccess)
{
return Ok(result.Value);
}
return NotFound(result.Errors);
}
[HttpPost]
public async Task<IActionResult> Create([FromBody] ProductDto product)
{
var result = await _productService.CreateAsync(product);
if (result.IsSuccess)
{
return CreatedAtAction(
nameof(GetById),
new { id = result.Value.Id },
result.Value);
}
return BadRequest(result.Errors);
}
[HttpPut("{id}")]
public async Task<IActionResult> Update(Guid id, [FromBody] ProductDto product)
{
var result = await _productService.UpdateAsync(id, product);
if (result.IsSuccess)
{
return Ok(result.Value);
}
return result.Status switch
{
ResultStatus.NotFound => NotFound(result.Errors),
_ => BadRequest(result.Errors)
};
}
[HttpDelete("{id}")]
public async Task<IActionResult> Delete(Guid id)
{
var result = await _productService.DeleteAsync(id);
if (result.IsSuccess)
{
return NoContent();
}
return result.Status switch
{
ResultStatus.NotFound => NotFound(result.Errors),
_ => BadRequest(result.Errors)
};
}
}
Using Result Pattern
[ApiController]
[Route("api/[controller]")]
public class OrdersController : NabsControllerBase
{
private readonly IOrderService _orderService;
public OrdersController(IOrderService orderService)
{
_orderService = orderService;
}
[HttpPost]
public async Task<IActionResult> PlaceOrder([FromBody] OrderRequest request)
{
var result = await _orderService.PlaceOrderAsync(request);
return result.Status switch
{
ResultStatus.Ok => Ok(result.Value),
ResultStatus.Invalid => BadRequest(result.ValidationErrors),
ResultStatus.NotFound => NotFound(result.Errors),
ResultStatus.Forbidden => Forbid(),
ResultStatus.Unauthorized => Unauthorized(),
_ => StatusCode(500, "An error occurred")
};
}
[HttpGet("{id}/status")]
public async Task<IActionResult> GetOrderStatus(Guid id)
{
var result = await _orderService.GetOrderStatusAsync(id);
if (!result.IsSuccess)
{
return NotFound(new { message = "Order not found", orderId = id });
}
return Ok(result.Value);
}
}
RESTful API with CRUD Operations
[ApiController]
[Route("api/[controller]")]
public class CustomersController : NabsControllerBase
{
private readonly ICustomerService _customerService;
private readonly ILogger<CustomersController> _logger;
public CustomersController(
ICustomerService customerService,
ILogger<CustomersController> logger)
{
_customerService = customerService;
_logger = logger;
}
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<CustomerDto>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetAll(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 10)
{
_logger.LogInformation("Fetching customers: page {Page}, size {PageSize}", page, pageSize);
var result = await _customerService.GetPagedAsync(page, pageSize);
return Ok(result);
}
[HttpGet("{id}")]
[ProducesResponseType(typeof(CustomerDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> GetById(Guid id)
{
var result = await _customerService.GetByIdAsync(id);
if (result.IsSuccess)
{
return Ok(result.Value);
}
_logger.LogWarning("Customer not found: {CustomerId}", id);
return NotFound();
}
[HttpPost]
[ProducesResponseType(typeof(CustomerDto), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Create([FromBody] CreateCustomerRequest request)
{
var result = await _customerService.CreateAsync(request);
if (result.IsSuccess)
{
return CreatedAtAction(
nameof(GetById),
new { id = result.Value.Id },
result.Value);
}
return BadRequest(result.Errors);
}
[HttpPut("{id}")]
[ProducesResponseType(typeof(CustomerDto), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Update(Guid id, [FromBody] UpdateCustomerRequest request)
{
var result = await _customerService.UpdateAsync(id, request);
return result.Status switch
{
ResultStatus.Ok => Ok(result.Value),
ResultStatus.NotFound => NotFound(),
ResultStatus.Invalid => BadRequest(result.ValidationErrors),
_ => BadRequest(result.Errors)
};
}
[HttpDelete("{id}")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> Delete(Guid id)
{
var result = await _customerService.DeleteAsync(id);
if (result.IsSuccess)
{
return NoContent();
}
return NotFound();
}
}
Handling Validation
[ApiController]
[Route("api/[controller]")]
public class UsersController : NabsControllerBase
{
private readonly IUserService _userService;
private readonly IValidator<CreateUserRequest> _validator;
public UsersController(
IUserService userService,
IValidator<CreateUserRequest> validator)
{
_userService = userService;
_validator = validator;
}
[HttpPost]
public async Task<IActionResult> CreateUser([FromBody] CreateUserRequest request)
{
// Validate request using FluentValidation
var validationResult = await _validator.ValidateAsync(request);
if (!validationResult.IsValid)
{
return BadRequest(validationResult.Errors);
}
var result = await _userService.CreateUserAsync(request);
if (result.IsSuccess)
{
return CreatedAtAction(
nameof(GetUser),
new { id = result.Value.Id },
result.Value);
}
return BadRequest(result.Errors);
}
[HttpGet("{id}")]
public async Task<IActionResult> GetUser(Guid id)
{
var result = await _userService.GetUserAsync(id);
if (result.IsSuccess)
{
return Ok(result.Value);
}
return NotFound();
}
}
API Versioning
[ApiController]
[ApiVersion("1.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class ItemsControllerV1 : NabsControllerBase
{
private readonly IItemService _itemService;
public ItemsControllerV1(IItemService itemService)
{
_itemService = itemService;
}
[HttpGet]
public async Task<IActionResult> GetAll()
{
var items = await _itemService.GetAllAsync();
return Ok(items);
}
}
[ApiController]
[ApiVersion("2.0")]
[Route("api/v{version:apiVersion}/[controller]")]
public class ItemsControllerV2 : NabsControllerBase
{
private readonly IItemServiceV2 _itemService;
public ItemsControllerV2(IItemServiceV2 itemService)
{
_itemService = itemService;
}
[HttpGet]
public async Task<IActionResult> GetAll([FromQuery] ItemFilter filter)
{
var items = await _itemService.GetAllAsync(filter);
return Ok(items);
}
}
Authentication & Authorization
using Microsoft.AspNetCore.Authorization;
[ApiController]
[Route("api/[controller]")]
[Authorize]
public class SecureController : NabsControllerBase
{
private readonly ISecureService _secureService;
public SecureController(ISecureService secureService)
{
_secureService = secureService;
}
[HttpGet("public")]
[AllowAnonymous]
public IActionResult GetPublicData()
{
return Ok(new { message = "This is public data" });
}
[HttpGet("private")]
public async Task<IActionResult> GetPrivateData()
{
var userId = User.FindFirst("sub")?.Value;
var data = await _secureService.GetUserDataAsync(userId);
return Ok(data);
}
[HttpGet("admin")]
[Authorize(Roles = "Admin")]
public async Task<IActionResult> GetAdminData()
{
var data = await _secureService.GetAdminDataAsync();
return Ok(data);
}
[HttpPost("policy-protected")]
[Authorize(Policy = "RequireSpecificClaim")]
public async Task<IActionResult> PolicyProtectedEndpoint([FromBody] SensitiveRequest request)
{
var result = await _secureService.ProcessSensitiveDataAsync(request);
return Ok(result);
}
}
File Upload/Download
[ApiController]
[Route("api/[controller]")]
public class FilesController : NabsControllerBase
{
private readonly IFileService _fileService;
public FilesController(IFileService fileService)
{
_fileService = fileService;
}
[HttpPost("upload")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> Upload(IFormFile file)
{
if (file == null || file.Length == 0)
{
return BadRequest("No file uploaded");
}
var result = await _fileService.SaveFileAsync(file);
if (result.IsSuccess)
{
return Ok(new { fileId = result.Value.Id, fileName = result.Value.Name });
}
return BadRequest(result.Errors);
}
[HttpGet("{id}")]
public async Task<IActionResult> Download(Guid id)
{
var result = await _fileService.GetFileAsync(id);
if (!result.IsSuccess)
{
return NotFound();
}
var file = result.Value;
return File(file.Content, file.ContentType, file.FileName);
}
}
API Reference
NabsControllerBase
Class Definition
public class NabsControllerBase : ControllerBase
A base controller class that extends ASP.NET Core's ControllerBase to provide a foundation for building API controllers with standardized patterns.
Inheritance:
ControllerBase(ASP.NET Core)
Usage:
All API controllers should inherit from NabsControllerBase to maintain consistency across the application.
[ApiController]
[Route("api/[controller]")]
public class MyController : NabsControllerBase
{
// Controller implementation
}
Architecture and Design Patterns
Result Pattern Integration
The library is designed to work seamlessly with the Ardalis.Result pattern, which provides a standardized way to return success/failure results from service methods.
Benefits:
- Explicit error handling without exceptions
- Type-safe return values
- Standardized error information
- Better API response consistency
RESTful Design
The library encourages RESTful API design following these principles:
- Resource-based URLs:
/api/products,/api/orders - HTTP verb semantics: GET (read), POST (create), PUT (update), DELETE (remove)
- Proper status codes: 200 OK, 201 Created, 204 No Content, 400 Bad Request, 404 Not Found
- HATEOAS-ready: Support for hypermedia links (optional)
Controller Organization
Recommended Structure:
Controllers/
├── ProductsController.cs # Product management
├── OrdersController.cs # Order processing
├── CustomersController.cs # Customer management
└── UsersController.cs # User management
Configuration Best Practices
Program.cs Setup
var builder = WebApplication.CreateBuilder(args);
// Add services
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Add your services
builder.Services.AddScoped<IProductService, ProductService>();
builder.Services.AddScoped<IOrderService, OrderService>();
var app = builder.Build();
// Configure middleware
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
await app.RunAsync();
Response Caching
[ApiController]
[Route("api/[controller]")]
public class CachedController : NabsControllerBase
{
[HttpGet]
[ResponseCache(Duration = 60, Location = ResponseCacheLocation.Any)]
public async Task<IActionResult> GetCachedData()
{
var data = await GetDataAsync();
return Ok(data);
}
}
CORS Configuration
// In Program.cs
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll",
builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
app.UseCors("AllowAll");
Error Handling
Global Exception Handling
// Create a middleware for global exception handling
public class ExceptionHandlingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<ExceptionHandlingMiddleware> _logger;
public ExceptionHandlingMiddleware(
RequestDelegate next,
ILogger<ExceptionHandlingMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
_logger.LogError(ex, "An unhandled exception occurred");
await HandleExceptionAsync(context, ex);
}
}
private static Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
var response = new
{
status = context.Response.StatusCode,
message = "An error occurred processing your request.",
detail = exception.Message
};
return context.Response.WriteAsJsonAsync(response);
}
}
// Register in Program.cs
app.UseMiddleware<ExceptionHandlingMiddleware>();
Controller-Level Error Handling
[ApiController]
[Route("api/[controller]")]
public class RobustController : NabsControllerBase
{
private readonly ILogger<RobustController> _logger;
public RobustController(ILogger<RobustController> logger)
{
_logger = logger;
}
[HttpGet("{id}")]
public async Task<IActionResult> Get(Guid id)
{
try
{
var result = await GetDataAsync(id);
if (result.IsSuccess)
{
return Ok(result.Value);
}
return NotFound();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error retrieving data for {Id}", id);
return StatusCode(500, "An error occurred");
}
}
}
Best Practices
1. Use Async/Await Consistently
// Good
[HttpGet]
public async Task<IActionResult> GetAll()
{
var items = await _service.GetAllAsync();
return Ok(items);
}
// Avoid
[HttpGet]
public IActionResult GetAll()
{
var items = _service.GetAll(); // Blocking call
return Ok(items);
}
2. Implement Proper Status Codes
// 200 OK - Successful GET/PUT
return Ok(data);
// 201 Created - Successful POST
return CreatedAtAction(nameof(GetById), new { id }, data);
// 204 No Content - Successful DELETE
return NoContent();
// 400 Bad Request - Invalid input
return BadRequest(errors);
// 404 Not Found - Resource not found
return NotFound();
// 500 Internal Server Error - Unhandled error
return StatusCode(500, "Internal server error");
3. Use DTOs for Request/Response
// Request DTO
public record CreateProductRequest(string Name, decimal Price, string Category);
// Response DTO
public record ProductDto(Guid Id, string Name, decimal Price, string Category, DateTime CreatedAt);
[HttpPost]
public async Task<IActionResult> Create([FromBody] CreateProductRequest request)
{
var result = await _service.CreateAsync(request);
return CreatedAtAction(nameof(GetById), new { id = result.Value.Id }, result.Value);
}
4. Add XML Documentation
/// <summary>
/// Gets all products from the catalog
/// </summary>
/// <param name="page">Page number (default: 1)</param>
/// <param name="pageSize">Items per page (default: 10)</param>
/// <returns>A list of products</returns>
/// <response code="200">Returns the list of products</response>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<ProductDto>), StatusCodes.Status200OK)]
public async Task<IActionResult> GetAll([FromQuery] int page = 1, [FromQuery] int pageSize = 10)
{
var products = await _service.GetPagedAsync(page, pageSize);
return Ok(products);
}
5. Implement Input Validation
public record CreateProductRequest
{
[Required]
[StringLength(100, MinimumLength = 3)]
public string Name { get; init; }
[Range(0.01, 999999.99)]
public decimal Price { get; init; }
[Required]
public string Category { get; init; }
}
Testing
Unit Testing Controllers
public class ProductsControllerTests
{
private readonly Mock<IProductService> _mockService;
private readonly ProductsController _controller;
public ProductsControllerTests()
{
_mockService = new Mock<IProductService>();
_controller = new ProductsController(_mockService.Object);
}
[Fact]
public async Task GetById_ExistingId_ReturnsOk()
{
// Arrange
var productId = Guid.NewGuid();
var product = new ProductDto(productId, "Test Product", 99.99m, "Electronics", DateTime.UtcNow);
_mockService.Setup(s => s.GetByIdAsync(productId))
.ReturnsAsync(Result<ProductDto>.Success(product));
// Act
var result = await _controller.GetById(productId);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnedProduct = Assert.IsType<ProductDto>(okResult.Value);
Assert.Equal(productId, returnedProduct.Id);
}
[Fact]
public async Task GetById_NonExistingId_ReturnsNotFound()
{
// Arrange
var productId = Guid.NewGuid();
_mockService.Setup(s => s.GetByIdAsync(productId))
.ReturnsAsync(Result<ProductDto>.NotFound());
// Act
var result = await _controller.GetById(productId);
// Assert
Assert.IsType<NotFoundObjectResult>(result);
}
}
Integration Testing
public class ApiIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
private readonly HttpClient _client;
public ApiIntegrationTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async Task GetProducts_ReturnsSuccessStatusCode()
{
// Act
var response = await _client.GetAsync("/api/products");
// Assert
response.EnsureSuccessStatusCode();
Assert.Equal("application/json; charset=utf-8",
response.Content.Headers.ContentType?.ToString());
}
}
Dependencies
- Microsoft.AspNetCore.App: Core ASP.NET Core framework reference
- Ardalis.Result: Result pattern implementation for consistent error handling
Integration with Other Libraries
With Nabs.Launchpad.Core.Persistence
[ApiController]
[Route("api/[controller]")]
public class DataController : NabsControllerBase
{
private readonly IDbContextFactory<AppDbContext> _contextFactory;
public DataController(IDbContextFactory<AppDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
[HttpGet]
public async Task<IActionResult> GetData()
{
await using var context = await _contextFactory.CreateDbContextAsync();
var data = await context.Entities.ToListAsync();
return Ok(data);
}
}
With Nabs.Launchpad.Core.Gateway
Controllers can be accessed through the API Gateway using YARP routing:
{
"ReverseProxy": {
"Routes": {
"api-route": {
"ClusterId": "api-cluster",
"Match": {
"Path": "/api/{**catch-all}"
}
}
}
}
}
With Nabs.Launchpad.Core.Serialisation
[ApiController]
[Route("api/[controller]")]
public class SerializationController : NabsControllerBase
{
[HttpPost]
public IActionResult ProcessJson([FromBody] JsonElement data)
{
var json = DefaultJsonSerializer.Serialize(data);
// Process JSON
return Ok(json);
}
}
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | 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. |
-
net10.0
- Ardalis.Result (>= 10.1.0)
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 | |
|---|---|---|---|
| 10.0.239 | 0 | 6/3/2026 | |
| 10.0.234 | 46 | 5/31/2026 | |
| 10.0.233 | 52 | 5/31/2026 | |
| 10.0.232 | 50 | 5/30/2026 | |
| 10.0.230 | 54 | 5/30/2026 | |
| 10.0.229 | 48 | 5/30/2026 | |
| 10.0.228 | 50 | 5/30/2026 | |
| 10.0.226 | 97 | 4/26/2026 | |
| 10.0.221 | 109 | 2/3/2026 | |
| 10.0.220 | 111 | 1/14/2026 | |
| 10.0.219 | 124 | 1/5/2026 | |
| 10.0.218 | 115 | 1/4/2026 | |
| 10.0.217 | 135 | 1/4/2026 | |
| 10.0.216 | 135 | 1/4/2026 | |
| 10.0.215 | 134 | 1/4/2026 | |
| 10.0.214 | 159 | 1/1/2026 | |
| 10.0.213 | 130 | 1/1/2026 | |
| 10.0.212 | 132 | 1/1/2026 | |
| 10.0.211 | 138 | 12/31/2025 | |
| 10.0.210 | 136 | 12/30/2025 |