MY.QuickAPI 1.0.1

dotnet add package MY.QuickAPI --version 1.0.1
                    
NuGet\Install-Package MY.QuickAPI -Version 1.0.1
                    
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="MY.QuickAPI" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="MY.QuickAPI" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="MY.QuickAPI" />
                    
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 MY.QuickAPI --version 1.0.1
                    
#r "nuget: MY.QuickAPI, 1.0.1"
                    
#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.
#addin nuget:?package=MY.QuickAPI&version=1.0.1
                    
Install MY.QuickAPI as a Cake Addin
#tool nuget:?package=MY.QuickAPI&version=1.0.1
                    
Install MY.QuickAPI as a Cake Tool

MY.QuickAPI

MY.QuickAPI is a .NET library that provides a set of powerful attributes for configuring database tables, columns, and constraints in a code-first approach. It simplifies the process of defining database schemas through C# attributes and automatically generates CRUD API endpoints.

Database Attributes

Table Configuration

SqlConstraintIndexAttribute

Defines indexes on database tables with support for both clustered and non-clustered indexes.

[SqlConstraintIndex(nameof(Email), IsUnique = true)]
[SqlConstraintIndex(nameof(LastName), nameof(FirstName), IsClustered = true)]
public class User 
{
    public string Email { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}
SqlPrimaryKeyAttribute

Customizes the primary key configuration when you need a different setup than the default.

[SqlPrimaryKey(nameof(TenantId), nameof(UserId), Name = "PK_TenantUser")]
public class TenantUser
{
    public int TenantId { get; set; }
    public int UserId { get; set; }
}

Column Configuration

SqlComputedAttribute

Defines computed columns with optional persistence.

public class Employee
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    [SqlComputed("[FirstName] + ' ' + [LastName]", Stored = true)]
    public string FullName { get; set; }
}
SqlDefaultValueAttribute

Sets default values for columns using SQL expressions or literal values.

public class Article
{
    public string Title { get; set; }
    
    [SqlDefaultValue("GETUTCDATE()")]
    public DateTime CreatedAt { get; set; }
    
    [SqlDefaultValue("0")]
    public int ViewCount { get; set; }
}

Endpoint Generation

Automatic CRUD Endpoints

QuickAPI can automatically generate CRUD endpoints for your models by adding the EndpointDefinitionAttribute.

[EndpointDefinition]
public class Product : BaseModel
{
    public string Name { get; set; }
    public decimal Price { get; set; }
    public string Description { get; set; }
}

This will generate the following endpoints:

  • GET /api/Product - Get a product by ID
  • GET /api/Products - Get multiple products with filtering and sorting
  • POST /api/Product - Create a new product
  • PUT /api/Product - Update an existing product
  • DELETE /api/Product - Delete a product
  • POST /api/Products - Create multiple products at once

Using DTOs for API Endpoints

You can use DTOs for input/output models in your API endpoints in two different ways:

Approach 1: Using the EndpointDefinition Attribute with DtoType

The simplest approach is to specify the DTO type directly on your model:

// Define your DTO
public record ProductDto : BaseDto
{
    public Guid CategoryId { get; set; }
    public string? CategoryName { get; set; }
}

// Create mapper implementation
public class ProductMapper : IModelDtoMapper<Product, ProductDto>
{
    private readonly ILogger<ProductMapper> _logger;
    private readonly BaseContext _context;

    public ProductMapper(ILogger<ProductMapper> logger, BaseContext context)
    {
        _logger = logger;
        _context = context;
    }

    public ProductDto MapToDto(Product model)
    {
        return new ProductDto
        {
            Id = model.Id,
            Code = model.Name,
            Info = model.Description ?? string.Empty,
            CategoryId = model.CategoryId,
            CategoryName = model.Category?.Name
        };
    }

    public Product MapToModel(ProductDto dto, Product? model = null)
    {
        model ??= new Product();
        model.Id = dto.Id;
        model.Name = dto.Code;
        model.Description = dto.Info;
        model.CategoryId = dto.CategoryId;
        return model;
    }

    // Implement collection mapping methods...
}

// Register the DTO with your model using the attribute
[EndpointDefinition(
    CrudOperation = CrudOperation.All, 
    DtoType = typeof(ProductDto) // Specify the DTO type here
)]
public class Product : BaseModel, ITenantModel
{
    public Guid TenantId { get; set; }
    public Guid CategoryId { get; set; }
    public virtual Category? Category { get; set; }
}

// Register the mapper in a Definition class
public class MappersDefinition : IDefinition
{
    public void DefineServices(IServiceCollection services)
    {
        services.AddTransient<IModelDtoMapper<Product, ProductDto>, ProductMapper>();
    }
    
    // other methods...
}
Approach 2: Creating a Custom Endpoint Definition

For more control, you can create a custom endpoint definition:

// Define your model and disable automatic endpoint creation
[EndpointDefinition(AutomaticEndpointCreation = false)]
public class Category : BaseModel, ITenantModel
{
    public Guid TenantId { get; set; }
}

// Define your DTO
public record CategoryDto : BaseDto
{
    public int? ProductCount { get; set; }
}

// Create your mapper
public class CategoryMapper : IModelDtoMapper<Category, CategoryDto>
{
    public CategoryDto MapToDto(Category model)
    {
        return new CategoryDto
        {
            Id = model.Id,
            Code = model.Name,
            Info = model.Description ?? string.Empty
        };
    }
    
    // Other mapper methods...
}

// Create a custom endpoint definition
public class CategoryEndpoint : BaseDtoEndpointDefinition<Category, CategoryDto>
{
    public CategoryEndpoint(
        BaseContext context,
        ILogger<CategoryEndpoint> logger,
        IModelDtoMapper<Category, CategoryDto> mapper) 
        : base(context, logger, mapper)
    {
        // Configure CRUD operations
        CrudOperation = CrudOperation.All;
        
        // Configure authorization
        RequireAuthorization = true;
        CommonRole = "Admin";
        
        // Set up event hooks
        OnBeforeGetMany = async (principal, options) =>
        {
            Logger.LogInformation("Custom before-hook executed");
            await Task.CompletedTask;
        };
    }

    public override void DefineServices(IServiceCollection services)
    {
        // Register the mapper here
        services.AddTransient<IModelDtoMapper<Category, CategoryDto>, CategoryMapper>();
    }
    
    // Override methods for custom behavior
    protected override async Task<IResult> GetManyAsync(
        ClaimsPrincipal claimsPrincipal, 
        BindableDataSourceLoadOptions options)
    {
        // Custom behavior 
        return await base.GetManyAsync(claimsPrincipal, options);
    }
}

Usage Examples

Creating a Table with Multiple Indexes

[SqlConstraintIndex(nameof(Email), IsUnique = true, Name = "UQ_User_Email")]
[SqlConstraintIndex(nameof(LastName), nameof(FirstName), IsClustered = true, Name = "IX_User_Name")]
public class User
{
    public int Id { get; set; }
    public string Email { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    
    [SqlDefaultValue("GETUTCDATE()")]
    public DateTime CreatedAt { get; set; }
    
    [SqlComputed("[FirstName] + ' ' + [LastName]")]
    public string FullName { get; set; }
}

Customizing Authorization for Endpoints

[EndpointDefinition(
    CommonRole = "Manager",
    GetRole = "User",
    PostRole = "Admin",
    AllowAnonymousGet = true
)]
public class Customer : BaseModel
{
    public string Name { get; set; }
    public string Email { get; set; }
}

Features

  • Automatic API Generation: Generate CRUD endpoints with a single attribute
  • DTO Support: Use Data Transfer Objects for API input/output
  • Flexible Index Configuration: Create unique, clustered, and non-clustered indexes
  • Computed Columns: Define computed columns with optional persistence
  • Default Values: Specify SQL default values for columns
  • Custom Primary Keys: Configure composite primary keys with custom naming
  • Role-Based Authorization: Configure endpoint access by role

Best Practices

  1. DTO Mapping:

    • Create dedicated DTO classes for external API communication
    • Implement custom mappers for precise control over property mapping
    • Register mappers in your service configuration
  2. Index Naming:

    • Use meaningful names for indexes
    • Follow a consistent naming convention (e.g., IX_TableName_Columns for non-unique indexes)
    • Use UQ prefix for unique indexes
    • Always use nameof operator when referencing properties to maintain type safety
  3. Authorization:

    • Use role-based authorization to secure endpoints
    • Configure different roles for different HTTP methods as needed
    • Consider which endpoints can be anonymous and which require authentication

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Product 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. 
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
1.0.1 214 3/7/2025
1.0.0 201 3/4/2025