Nabs.Launchpad.Core.Apis 10.0.220

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

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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (1)

Showing the top 1 NuGet packages that depend on Nabs.Launchpad.Core.Apis:

Package Downloads
Nabs.Launchpad.Core.FeatureFlags

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
10.0.286 44 9/18/2026
10.0.285 42 9/18/2026
10.0.273 114 9/6/2026
10.0.272 102 9/5/2026
10.0.270 99 9/5/2026
10.0.269 97 9/5/2026
10.0.255 116 7/26/2026
10.0.250 120 6/23/2026
10.0.249 121 6/6/2026
10.0.248 116 6/6/2026
10.0.247 116 6/6/2026
10.0.246 124 6/6/2026
10.0.242 111 6/4/2026
10.0.241 110 6/4/2026
10.0.240 114 6/4/2026
10.0.239 110 6/3/2026
10.0.234 110 5/31/2026
10.0.233 112 5/31/2026
10.0.232 110 5/30/2026
10.0.220 121 1/14/2026
Loading failed