Russkyc.MinimalApi.Framework
0.9.0
Prefix Reserved
dotnet add package Russkyc.MinimalApi.Framework --version 0.9.0
NuGet\Install-Package Russkyc.MinimalApi.Framework -Version 0.9.0
<PackageReference Include="Russkyc.MinimalApi.Framework" Version="0.9.0" />
paket add Russkyc.MinimalApi.Framework --version 0.9.0
#r "nuget: Russkyc.MinimalApi.Framework, 0.9.0"
// Install Russkyc.MinimalApi.Framework as a Cake Addin #addin nuget:?package=Russkyc.MinimalApi.Framework&version=0.9.0 // Install Russkyc.MinimalApi.Framework as a Cake Tool #tool nuget:?package=Russkyc.MinimalApi.Framework&version=0.9.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:
- Realtime support using SignalR learn more...
- Advanced querying with filtering, ordering, and pagination learn more...
- Batch endpoints for adding, updating, and deleting multiple entities 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.
Create a new ASP.NET Core Web API project if you don't already have one.
Add the required services and API endpoint mappings in the
Program.cs
file: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.AddDbContextService(assembly, options => options.UseInMemoryDatabase("sample")); // Uncomment to enable realtime events // by default the endpoint used is "/crud-events" // this can be changed by providing a string parameter, eg; `MapRealtimeHub("/api-events")` builder.Services.AddRealtimeService(); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); // Map Entity CRUD Endpoints app.MapGroup("api") .MapAllEntityEndpoints<int>(assembly); // Map Realtime Hub app.MapRealtimeHub(); 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()
beforeDatabase.EnsureCreated()
As an example:
builder.Services.AddDbContextService(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 request to the endpoint /api/sampleentity
you will
receive 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.
Ordering Support
Entities can now be ordered using the orderBy
and orderByDescending
query parameters. Multiple properties can be specified for ordering, separated by commas. The first property will be ordered using OrderBy
or OrderByDescending
, and subsequent properties will be ordered using ThenBy
or ThenByDescending
.
GET /api/sampleentity?orderBy=property,embeddedEntity.property2&orderByDescending=true
the orderBy
query param will define what properties are taken into consideration in ordering.
the orderByDescending
query param is a bool property that changes the behavior to descending when set to true.
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
Realtime Support
To enable realtime events support, add the AddRealtimeService
method to your service collection and map the realtime hub using MapRealtimeHub
in your Program.cs
file:
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.AddDbContextService(assembly, options => options.UseInMemoryDatabase("sample"));
// Add Realtime Service
builder.Services.AddRealtimeService();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
// Map Entity CRUD Endpoints
app.MapGroup("api")
.MapAllEntityEndpoints<int>(assembly);
// Map Realtime Hub
app.MapRealtimeHub();
app.Run();
The default websocket endpoint is /crud-events
but can be changed by
adding a string parameter of the desired endpint in the MapRealtimeHub
method, eg; .MapRealtimeHub("/api-events")
.
Each event returned is an EntityEvent<T>
type, where T is the type of data
returned by the resource event that triggered the websocket message. Eg; when
creating a SampleEntity
using post
the EntityEvent
being sent in realtime is of
type EntityEvent<SampleEntity>
.
EntityEvent<T> Class
public class EntityEvent<T>
{
public string Resource { get; set; }
public string Type { get; set; }
public T? Data { get; set; }
}
Schema Information
- The
Resource
property will be the name of the entity in lowercase, eg; "sampleentity". - The
Type
property will be the type of event, eg;created
,updated
,deleted
,batch-created
,batch-updated
,batch-deleted
- The
Data
property contains the data being returned by that resource method.
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 | 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. |
-
net8.0
- Microsoft.AspNetCore.OpenApi (>= 8.0.10)
- Microsoft.EntityFrameworkCore (>= 8.0.10)
- Microsoft.EntityFrameworkCore.InMemory (>= 8.0.10)
- Microsoft.EntityFrameworkCore.Relational (>= 8.0.10)
- Russkyc.MinimalApi.Framework.Core (>= 0.9.0)
- System.Linq.Dynamic.Core (>= 1.4.5)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
New Additions:
- Add realtime CRUD events
- Add query ordering support
Read more in the updated documentation