Russkyc.MinimalApi.Framework 0.8.0

Prefix Reserved
There is a newer version of this package available.
See the version list below for details.
dotnet add package Russkyc.MinimalApi.Framework --version 0.8.0                
NuGet\Install-Package Russkyc.MinimalApi.Framework -Version 0.8.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="Russkyc.MinimalApi.Framework" Version="0.8.0" />                
For projects that support PackageReference, copy this XML node into the project file to reference the package.
paket add Russkyc.MinimalApi.Framework --version 0.8.0                
#r "nuget: Russkyc.MinimalApi.Framework, 0.8.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.
// Install Russkyc.MinimalApi.Framework as a Cake Addin
#addin nuget:?package=Russkyc.MinimalApi.Framework&version=0.8.0

// Install Russkyc.MinimalApi.Framework as a Cake Tool
#tool nuget:?package=Russkyc.MinimalApi.Framework&version=0.8.0                

Russkyc.MinimalApi.Framework - A Generic API Crud Generator for EntityFrameworkCore

Dynamically generates a generic CRUD API implementation backed with Entity Framework Core and Minimal API. This can be used to create a quick backend for prototyping apps that use CRUD operations.

✨ What's New:

Migrations support (using custom DbContext implementation) learn more...

Potential use-cases

  • Quick API prototyping
  • Small projects that only require CRUD functionality
  • Frontend Testing (if a backend API is needed)

Getting Started

Installation

To install the Russkyc.MinimalApi.Framework package, you can use the NuGet Package Manager or the .NET CLI.

Using .NET CLI

Run the following command in your terminal:

dotnet add package Russkyc.MinimalApi.Framework

Setup

Follow these steps to set up the Russkyc.MinimalApi.Framework in your project.

  1. Create a new ASP.NET Core Web API project if you don't already have one.

  2. Add the required services and API endpoint mappings in the Program.cs file:

    // Used by reflection to know where to find entity classes
    var assembly = Assembly.GetExecutingAssembly();
    
    // 1. Entity Context Services
    // `options.UseInMemoryDatabase()` can be replaced with other EF Core Data Providers
    builder.Services.AddDbContextService(assembly, options => options.UseInMemoryDatabase("sample"));
    
    // 2. Map Entity CRUD Endpoints
    // `MapGroup()` is not required, used to add the "/api" prefix.
    // Can simply be `app.MapAllEntityEndpoints<int>(assembly);`
    app.MapGroup("api")
        .MapAllEntityEndpoints<int>(assembly);
    

    This is how the full Program.cs might look like

    using System.Reflection;
    using Microsoft.EntityFrameworkCore;
    using Russkyc.MinimalApi.Framework;
    
    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddEndpointsApiExplorer();
    builder.Services.AddSwaggerGen();
    
    var assembly = Assembly.GetExecutingAssembly();
    
    // Add Entity Context Services
    builder.Services.AddAllEntityServices(assembly, options => options.UseInMemoryDatabase("sample"));
    
    var app = builder.Build();
    
    if (app.Environment.IsDevelopment())
    {
        app.UseSwagger();
        app.UseSwaggerUI();
    }
    
    app.UseHttpsRedirection();
    
    // Map Entity CRUD Endpoints
    app.MapGroup("api")
        .MapAllEntityEndpoints<int>(assembly);
    
    app.Run();
    

Setting-up entity classes

All entity classes should inherit from the DbEntity<TKeyType> abstract class. Where TKeyType is the Id type of the entity.

SampleEntity Class

public class SampleEntity : DbEntity<int>
{
    public string Property { get; set; }
    public virtual SampleEmbeddedEntity EmbeddedEntity { get; set; }
}

SampleEmbeddedEntity Class

public class SampleEmbeddedEntity : DbEntity<int>
{
    public string Property2 { get; set; }
}

You now have a fully working EntityFrameworkCore backed MinimalApi CRUD project.

Advanced Setup

Custom DbContext (Useful for migrations support)

To support database migrations, we can create a custom dbcontext class that inherits from BaseDbContext

using Microsoft.EntityFrameworkCore;
using Russkyc.MinimalApi.Framework;

namespace SampleProject;

public class CustomDbContext : BaseDbContext
{
    public CustomDbContext(DbContextOptions options) : base(options)
    {
    }
    
    // Entity collections are required to be defined
    // using the naming convention `<ClassName>Collection`
    
    public DbSet<SampleEmbeddedEntity> SampleEmbeddedEntityCollection { get; set; }
    public DbSet<SampleEntity> SampleEntityCollection { get; set; }
}

Database Initialization

These are different database action types that can be defined in the AddAllEntityServices or AddEntityServices extensions using the databaseAction parameter:

  • None - Default Action
  • DatabaseAction.EnsureCreated - Run Database.EnsureCreated()
  • DatabaseAction.DeleteAndCreate - Run Database.EnsureDeleted() before Database.EnsureCreated()

As an example:

builder.Services.AddAllEntityServices(assembly,
    options => options.UseSqlite("Data Source=test.sqlite"),
    databaseAction: DatabaseAction.DeleteAndCreate);

This ensures that a fresh database is used in every run. If you only need to set the database once, DatabaseAction.EnsureCreated is the recommended option

Advanced Route Options

You can modify the endpoint options using the routeOptionsAction parameter. For example, to require authorization for all endpoints:

app.MapGroup("api")
    .MapAllEntityEndpoints<int>(assembly, routeOptions => routeOptions.RequireAuthorization());

Advanced Querying

Apart from the standard CRUD api functionality, there is also some support for advanced querying.

Entity Framework Core Navigation Properties

If you do a get requrest to the endpoint /api/sampleentity you will recieve a response that looks like this:

[
  {
    "id": 1,
    "property": "Entity 1",
    "embeddedEntity": null
  },
  {
    "id": 2,
    "property": "Entity 2",
    "embeddedEntity": null
  },
  {
    "id": 3,
    "property": "Entity 3",
    "embeddedEntity": null
  },
  {
    "id": 4,
    "property": "Entity 4",
    "embeddedEntity": null
  }
]

This is because navigation properties for referenced entities are not automatically included (for performance purposes). you can use the include query parameter to include the referenced entity when needed.

GET /api/sampleentity?include=embeddedentity

Then you will have this result:

[
  {
    "id": 1,
    "property": "Entity 1",
    "embeddedEntity": {
      "id": 1,
      "property2": "Embedded Entity 1"
    }
  },
  {
    "id": 2,
    "property": "Entity 2",
    "embeddedEntity": {
      "id": 2,
      "property2": "Embedded Entity 2"
    }
  },
  {
    "id": 3,
    "property": "Entity 3",
    "embeddedEntity": {
      "id": 3,
      "property2": "Embedded Entity 3"
    }
  },
  {
    "id": 4,
    "property": "Entity 4",
    "embeddedEntity": {
      "id": 4,
      "property2": "Embedded Entity 4"
    }
  }
]
Filter query support (with the help of DynamicExpressionParser in System.Linq.Dynamic.Core)

entities can now be filtered with the filter queryParam and supports standard expressions. Parameters should be prefixed with @ in order to be valid, eg; a parameter named Content should be used as @Content. Here are a few examples:

GET /api/sampleentity?filter=@Content.StartsWith("hello")
GET /api/sampleentity?filter=@Content.StartsWith("hi") && !@Content.Contains("user")
GET /api/sampleentity?filter=@Count == 1 || @Count > 8
GET /api/sampleentity?filter=@ContactPerson != null

These are visualized for readability, in actual use, the filter value should be Url Encoded.

Pagination

By default pagination is disabled and the query collection response returns something like this

[
  {
    "id": 1,
    "property": "Entity 1",
    "embeddedEntity": {
      "id": 1,
      "property2": "Embedded Entity 1"
    }
  },
  {
    "id": 2,
    "property": "Entity 2",
    "embeddedEntity": {
      "id": 2,
      "property2": "Embedded Entity 2"
    }
  }
]

To enable pagination, set the paginate query param to true and set the page, pageSize query params as needed. as an example:

GET /api/sampleentity?paginate=true&page=1&pageSize=1

This will now return a PaginatedCollection object with this json schema:

{
  "data": [
    {
      "property": "Entity 1",
      "embeddedEntity": null,
      "id": "84e93f60-b2bc-4303-af0a-c51c205addb9"
    }
  ],
  "page": 1,
  "pageSize": 1,
  "totalRecords": 2,
  "totalPages": 2
}

Batch Endpoints

Batch endpoints are supported for adding, updating, and deleting multiple entities at once.

Batch Insert
POST /api/sampleentity/batch
Content-Type: application/json

[
  {
    "id": 1,
    "property": "Entity 1",
    "embeddedEntity": null
  },
  {
    "id": 2,
    "property": "Entity 2",
    "embeddedEntity": null
  }
]
Batch Update
PUT /api/sampleentity/batch
Content-Type: application/json

[
  {
    "id": 1,
    "property": "Updated Entity 1",
    "embeddedEntity": null
  },
  {
    "id": 2,
    "property": "Updated Entity 2",
    "embeddedEntity": null
  }
]
Batch Update with Filters and Dynamic Fields
PATCH /api/sampleentity/batch?filter=@property.Contains("Old")
Content-Type: application/json

{
  "property": "Updated Value"
}
Batch Delete
DELETE /api/sampleentity/batch?filter=@Count > 8

Important things to consider

  • When using generic implementations like this on the server side, business logic is now moved into the client and becomes a client concern.
  • If your API needs to do complex business logic over the CRUD functionality, please consider implementing custom endpoints instead of using generic endpoints such as this.
  • There is currently no implementation for validation and DTO mapping, this can be added later as the project updates.
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. 
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.9.0 106 10/13/2024
0.8.0 97 10/12/2024
0.7.0 99 10/10/2024
0.6.0 95 10/9/2024
0.5.0 89 10/9/2024
0.4.0 88 10/9/2024
0.3.1 95 10/8/2024
0.3.0 90 10/8/2024
0.2.0 105 10/8/2024
0.1.1 89 10/8/2024
0.1.0 93 10/8/2024

- Refactor DbContext usage (migrate from per-entity context)
- Add migrations support (using a custom BaseDbContext implementation)