Delphi framework inspired by ASP.NET Core for building high-performance, scalable desktop, service and web applications. Windows · Linux · Android · macOS · iOS. Delphi 10 to 12 Athens.
Please "star" this project in GitHub! It costs nothing but helps to reference the code.
If you find this project useful, please consider making a donation.
- Features
- Requirements
- Installation
- Quick Start
- Dependency Injection
- Configuration & Options
- Entity ORM
- MVC / WebAPI
- Authentication & Authorization
- AutoMapper
- Caching
- Localization
- Background Services
- Health Checks
- Message Queue
- Samples
- Running Tests
- Unit Reference
| Area | Feature |
|---|---|
| DI / IoC | Singleton · Transient · Scoped · Named services · Keyed services · Factory delegates · IOptions<T> · IServiceScope |
| Entity ORM | SQLite · SQL Server · MySQL · MS Access · PostgreSQL · CRUD · LINQ · Migrations · Navigation properties · Soft delete · Optimistic concurrency · Repository · Unit of Work · Seeding |
| MVC / WebAPI | Attribute routing · [FromBody] · [FromQuery] · [FromHeader] · [FromForm] · Model validation · Action filters · Exception handler · Rate limiting · CORS · Compression · API versioning · Swagger/OpenAPI · Minimal API · Content negotiation |
| Auth | JWT Bearer · API Key · Cookie · OAuth2 / OpenID Connect · ASP.NET-style Identity · 2FA/TOTP · Resource-based authorization · Policies |
| AutoMapper | Profile maps · ForMember · ReverseMap · Condition · ConvertUsing · BeforeMap · AfterMap · NullSubstitute · Map context · AssertConfigurationIsValid |
| Caching | In-memory · Redis |
| Localization | IStringLocalization · Culture files · Accept-Language header |
| Background | IHostedService · IBackgroundService · ITaskControl (background + scheduled tasks) |
| Health Checks | URL · SQL Server · Redis · Entity DB · Dashboard |
| Message Queue | Redis producer/consumer · Reliable queue · Configurable pool |
| View Engine | Mustache templates · Layouts · Partials · Custom helpers |
| Session | In-memory session store · Session middleware · Cookie-based session ID |
- Delphi 10 (Seattle) through 12 Athens
- QuickLib — core utilities (required)
- QuickLogger — logging backend (required)
- FireDAC — for Entity engines (included with Delphi)
- Redis — only for Redis caching / message queue samples
- Open Delphinus in the Delphi IDE.
- Search for QuickCore and click Install.
- Delphinus will resolve and install QuickLib and QuickLogger automatically.
git clone https://github.com/exilon/QuickCore
git clone https://github.com/exilon/QuickLib
git clone https://github.com/exilon/QuickLogger
Add all three folders to Tools → Options → Library path in the Delphi IDE.
Create a minimal REST API in under 30 lines:
HelloWorld.dpr
program HelloWorld;
{$APPTYPE CONSOLE}
uses Quick.Core.Mvc, Startup in 'Startup.pas';
var Server : TMvcServer;
begin
Server := TMvcServer.Create('127.0.0.1', 8080, False);
Server.UseStartup<TStartup>;
Server.Start;
Readln;
Server.Free;
end.Startup.pas
unit Startup;
interface
uses Quick.Core.Mvc, Quick.Core.DependencyInjection, Controller.Home;
type
TStartup = class(TStartupMvc)
public
class procedure ConfigureServices(services : TServiceCollection); override;
class procedure Configure(app : TMVCServer); override;
end;
implementation
class procedure TStartup.ConfigureServices(services : TServiceCollection);
begin
services.AddLogging(TLoggerBuilder.GetBuilder.AddConsole.Build);
end;
class procedure TStartup.Configure(app : TMVCServer);
begin
app.AddControllers.AddController(THomeController).UseRouting.UseMVC;
end;
end.Controller.Home.pas
unit Controller.Home;
interface
uses Quick.Core.Mvc.Controller, Quick.Core.Mvc.ActionResult;
type
[Route('api')]
[ApiController]
THomeController = class(THttpController)
published
[HttpGet('hello')]
function Hello : IActionResult;
end;
implementation
function THomeController.Hello: IActionResult;
begin
Result := Json('{"message":"Hello, World!"}');
end;
initialization
RegisterController(THomeController);
end.GET http://localhost:8080/api/hello
→ {"message":"Hello, World!"}
QuickCore's built-in IoC container supports constructor injection, three lifetimes, named/keyed services and factory delegates.
// Singleton — same instance for the whole app lifetime
services.AddSingleton<IMyService, TMyService>;
// Transient — new instance on every resolution
services.AddTransient<IMyService, TMyService>;
// Scoped — one instance per request scope
services.AddScoped<IMyService, TMyService>;services.AddSingleton<IMyService, TMyService>('',
function : TMyService
begin
Result := TMyService.Create('some-param');
end);services.AddSingleton<ICache, TRedisCache>('redis');
services.AddSingleton<ICache, TMemoryCache>('memory');
// Resolve by name
var cache := provider.GetService<ICache>('redis');
// Inject by name in a controller action parameter
function MyAction([FromKeyedServices('redis')] cache : ICache) : IActionResult;// Register with defaults
services.Configure<TSmtpOptions>(procedure(o : TSmtpOptions)
begin
o.Host := 'localhost';
o.Port := 25;
end);
// Inject
constructor TMailService.Create(opts : IOptions<TSmtpOptions>);
begin
fHost := opts.Value.Host;
end;Options are persisted to a YAML (or JSON) file. Use the CORE_ENVIRONMENT environment variable to select environment-specific files (QuickCore.production.yaml, etc.).
services
.AddOptions(TOptionsFileFormat.ofYAML, True)
.Configure<TAppSettings>(procedure(o : TAppSettings)
begin
o.Smtp := 'mail.domain.com';
o.ApiUrl := 'https://api.example.com';
end);Environment variable expansion inside config files is supported: ${MY_SECRET_KEY}.
QuickCore includes a lightweight ORM similar to EF Core.
[Table('products')]
TProduct = class(TEntity)
private
FId : TAutoID;
FName : string;
FPrice : Double;
public
[Key]
property Id : TAutoID read FId write FId;
property Name : string read FName write FName;
property Price : Double read FPrice write FPrice;
end;TShopContext = class(TDBContext)
private
FProducts : TDBSet<TProduct>;
public
property Products : TDBSet<TProduct> read FProducts write FProducts;
constructor Create(aConn : TDBConnectionModel); override;
end;services.Extension<TEntityServiceExtension>
.AddDBContext<TShopContext>(procedure(o : TDBContextOptionsBuilder)
begin
o.UseSQLite.UseDatabase('shop.db');
end);// Inject context
constructor TProductsController.Create(ctx : TShopContext);
// Add
ctx.Products.Add(product);
// Query
var list := ctx.Products.Where('Price < ?', [100.0]).OrderBy('Name').Select;
// Update / Delete
ctx.Products.Update(product);
ctx.Products.Delete(product);type
TAddCategoryMigration = class(TMigration)
public
procedure Up; override;
procedure Down; override;
end;
procedure TAddCategoryMigration.Up;
begin
ExecuteSQL('ALTER TABLE products ADD COLUMN Category VARCHAR(100)');
end;// IRepository<T> is included — register via factory
services.AddScoped<IRepository<TProduct>, TEntityRepository<TProduct>>('',
function : TEntityRepository<TProduct>
begin
Result := TEntityRepository<TProduct>.Create(
services.Resolve<TShopContext>.Products, 'Id');
end);ctx.Database.BeginTransaction;
try
ctx.Orders.Add(order);
ctx.OrderItems.Add(item);
ctx.Database.CommitTransaction;
except
ctx.Database.RollbackTransaction;
raise;
end;// Soft delete — inherit from TEntitySD; deleted rows are filtered automatically
[SoftDelete]
TPost = class(TEntitySD) ...
// Optimistic concurrency — inherit from TEntityRV; conflict raises EOptimisticConcurrencyException
TProduct = class(TEntityRV) ...[Route('api/products')]
[ApiController]
TProductsController = class(THttpController)
published
[HttpGet]
function List : IActionResult;
[HttpGet('{id}')]
function GetById(id : Integer) : IActionResult;
[HttpPost]
function Create([FromBody] product : TProduct) : IActionResult;
[HttpPut('{id}')]
function Update(id : Integer; [FromBody] product : TProduct) : IActionResult;
[HttpDelete('{id}')]
function Delete(id : Integer) : IActionResult;
end;| Attribute | Source |
|---|---|
[FromBody] |
JSON request body (auto-deserialized) |
[FromQuery] |
Query string: ?name=value |
[FromHeader] |
Request header |
[FromForm] |
Multipart form / file upload |
| (path segment) | URL path parameter {id} |
TCreateProductDto = class
[Required]
[StringLength(100)]
property Name : string read ... write ...;
[Range(0.01, 1000000)]
property Price : Double read ... write ...;
end;
// Validation is automatic — returns HTTP 400 + JSON errors on failureResult := Ok; // 200
Result := Json(obj); // 200 + serialized JSON
Result := StatusCode(201, '...'); // custom status
Result := BadRequest('invalid input'); // 400
Result := NotFound; // 404
Result := Unauthorized; // 401
Result := Redirect('https://...'); // 302
Result := View('home'); // render Mustache viewapp
.UseExceptionHandler // global 500 handler
.UseCors(procedure(o : TCorsPolicyOptions) begin
o.AllowAnyOrigin.AllowAnyMethod.AllowAnyHeader;
end)
.UseRateLimiting(100, 60) // 100 req / 60 s
.UseResponseCompression
.UseAuthentication
.UseAuthorization
.UseSession
.UseSwagger // /swagger + /swagger/v1/swagger.json
.UseStaticFiles
.UseRouting
.UseMVC;app.UseSwagger;
// Browse: http://localhost:8080/swaggerEnrich operations with attributes:
[SwaggerOperation('List all products')]
[ProducesResponseType(200)]
[ProducesResponseType(401)]
function List : IActionResult;server.MapGet('/ping', function(ctx : THttpContext) : string
begin
Result := '{"pong":true}';
end);services.Extension<TAuthenticationServiceExtension>
.AddAuthentication
.AddJwtBearer(procedure(o : TJwtOptions)
begin
o.Secret := 'my-256-bit-secret';
o.Issuer := 'my-app';
o.ExpiryMinutes := 60;
end);
app.UseAuthentication.UseAuthorization;Generate a token:
// Inject IJwtTokenService
var token := fJwt.GenerateToken(userId, username, ['Admin']);services.Extension<TApiKeyAuthenticationServiceExtension>
.AddApiKey
.UseMemoryStore(procedure(o : TApiKeyMemoryStoreOptions)
begin
o.AddApiKey('Alice', 'Admin', 'secret-key-abc');
o.AddApiKey('Bob', 'User', 'secret-key-xyz');
end);services.Extension<TAuthorizationServiceExtension>
.AddAuthorization(procedure(o : TAuthorizationOptions)
begin
o.AddPolicy('AdminOnly',
TAuthorizationPolicyBuilder.GetBuilder
.RequireAuthenticatedUser
.RequireClaim(TClaimTypes.Role, 'Admin')
.Build);
end);
// In controller
[Authorize] // any authenticated user
[Authorize(Policy='AdminOnly')] // admin only
[AllowAnonymous] // bypass authservices.Extension<TAuthenticationServiceExtension>
.AddIdentity<TAppUser, TAppRole>(procedure(o : TIdentityOptions)
begin
o.Password.RequiredLength := 8;
o.User.RequireUniqueEmail := True;
end)
.AddEntityStore<TIdentityContext>;
// Inject IUserManager<TUser, TRole>
fUserManager.CreateUser(user, password);
fUserManager.ValidateCredentials(username, password);
fUserManager.AddToRole(userId, 'Admin');// Define a profile
constructor TMyProfile.Create;
begin
CreateMap<TDBProduct, TProductDto>
.ForMember('Name', 'FullName') // rename property
.ReverseMap; // also maps TProductDto → TDBProduct
end;
initialization
TAutoMapper.RegisterProfile<TMyProfile>;CreateMap<TDBProduct, TProductDto>
.Condition('Price', function(src : TObject) : Boolean // skip when condition false
begin Result := (src as TDBProduct).Price > 0; end)
.ConvertUsing('Category', function(v : TValue) : TValue // transform value
begin Result := TValue.From(v.AsString.ToUpper); end)
.BeforeMap(procedure(src, dst : TObject) begin ... end)
.AfterMap(procedure(src, dst : TObject) begin ... end)
.NullSubstitute('(none)')
.IgnoreOtherMembers;
// Validate all maps at startup
TAutoMapper.AssertConfigurationIsValid;constructor TController.Create(mapper : IMapper);
...
var dto := fMapper.Map<TProductDto>(dbProduct);services.Extension<TMemoryCacheServiceExtension>.AddMemoryCache;
// Inject IMemoryCache
if not fCache.TryGetValue<TList<TProduct>>('products', list) then
begin
list := GetFromDB;
fCache.SetValue<TList<TProduct>>('products', list, 60); // TTL 60 s
end;services.Extension<TRedisCacheServiceExtension>
.AddRedisCache(procedure(o : TRedisCacheOptions)
begin
o.Host := 'localhost';
o.Port := 6379;
o.DataBase := 0;
end);services.Extension<TLocalizationServiceExtension>
.AddLocalization(procedure(o : TLocalizationOptions)
begin
o.AddCulture('en');
o.AddCulture('es');
o.AddCulture('fr');
o.AddIfNotfound := True;
end);
// Inject IStringLocalization — culture selected from Accept-Language header
var msg := fL10n.GetItemFmt('greeting', [userName]);Place locale files in locales/ next to the executable:
locales/en.lng → greeting=Hello, %s!
locales/es.lng → greeting=¡Hola, %s!
locales/fr.lng → greeting=Bonjour, %s!
type
TMyWorker = class(TBackgroundService)
protected
procedure Execute; override;
end;
// Register
services.Extension<THostedServiceExtension>.AddHostedService<TMyWorker>;services.Extension<TTaskControlServiceExtension>.AddTaskControl;
// After build — queue an ad-hoc task
fTaskControl.BackgroundTasks.QueueTask(procedure begin
DoSomeWork;
end);
// Schedule a recurring task
fTaskControl.ScheduledTasks.AddTask('CleanUp')
.RunEvery(3600000) // every hour
.OnExecute(procedure begin CleanOldRecords; end);services
.Extension<THealthChecksServiceExtension>
.AddHealthChecks
.AddUrlCheck('google', 'https://www.google.com', TTimeSpan.FromSeconds(5))
.AddDBContextCheck<TShopContext>('db', TTimeSpan.FromSeconds(5))
.AddMemoryCheck('memory', 512, TTimeSpan.FromSeconds(30))
.AddInMemoryStorage;
app.Extension<THealthChecksUIMVCServerExtension>
.UseHealthChecksUI;
// GET /health → Xabaril-style summary JSON
// GET /health/live → liveness
// GET /healthui → HTML dashboardRedis-backed reliable producer/consumer queue:
services.Extension<TMessageQueueServiceExtension>
.AddRedisMessageQueue<TOrderProcessor>(procedure(o : TRedisMessageQueueOptions)
begin
o.Host := 'localhost';
o.Port := 6379;
o.Key := 'orders';
o.MaxConsumersPool := 3;
o.ReliableMessageQueue.Enabled := True;
end);
// Produce (inject IMessageQueue<TOrderMessage>)
fQueue.Enqueue(msg);
// Consume (TOrderProcessor extends TMessageQueueTask<TOrderMessage>)
procedure TOrderProcessor.ProcessMessage(aMessage : TOrderMessage);
begin
// called automatically for each dequeued message
end;24 ready-to-compile projects in samples/delphi/:
| # | Project | Description |
|---|---|---|
| S-01 | HelloWorldApi |
Minimal API — one controller, one GET endpoint |
| S-02 | DependencyInjection |
Singleton / Transient / Scoped lifetimes demo |
| S-03 | OptionsPattern |
TMyOptions, YAML config, IOptions<T> injection |
| S-04 | AutoMapperBasic |
Profile, CreateMap, ForMember, Map<T> |
| S-05 | EntityCRUD |
SQLite CRUD with TDBContext + TDBSet<T> |
| # | Project | Description |
|---|---|---|
| S-06 | WebApiWithDI |
REST API with IProductService injected in controller |
| S-07 | WebApiWithDatabase |
REST API + SQLite, YAML config |
| S-08 | WebApiWithLogging |
Console + file logging, ILogger injection |
| S-09 | WebApiWithCaching |
Cache-aside pattern with IMemoryCache |
| S-10 | WebApiWithAutoMapper |
DTO ↔ entity mapping in a REST pipeline |
| S-11 | CustomMiddleware |
Custom middleware class + inline Use(func) |
| S-12 | HealthChecksDashboard |
/health, /health/live, /health/ready and /healthui dashboard |
| # | Project | Description |
|---|---|---|
| S-13 | WebApiWithApiKeyAuth |
API Key auth, AdminOnly policy, [Authorize] / [AllowAnonymous] |
| S-14 | WebApiWithIdentity |
Full Identity: register/login, IUserManager, SQLite store |
| S-15 | CleanArchitectureApi |
Domain / Application / Infrastructure / Presentation layers |
| S-16 | BackgroundJobsService |
ITaskControl: background + scheduled tasks |
| S-17 | WindowsServiceApp |
--install / --remove as a Windows Service |
| S-18 | MultipleDbContexts |
Two independent SQLite TDBContext in one app |
| S-19 | MessageQueueRedis |
Redis producer/consumer with reliable queue |
| S-20 | LocalizationSample |
IStringLocalization + Accept-Language header |
| # | Project | Description |
|---|---|---|
| S-21 | RepositoryPattern |
IRepository<T> + TEntityRepository<T> wired via DI |
| S-22 | UnitOfWorkPattern |
Multi-entity atomic operation with BeginTransaction |
| S-23 | DecoratorPattern |
Caching decorator for IProductService via DI factory |
| S-24 | ChainOfResponsibility |
Pure validation pipeline: NotEmpty → Length → Price |
85 DUnitX tests covering DI, AutoMapper, Entity ORM, Security, MVC, and Caching:
tests/bin/Win64/Debug/QuickCore.Tests.exe
Or compile and run tests/QuickCore.Tests.dproj from the Delphi IDE.
| Unit | Description |
|---|---|
Quick.Core.Logging.Abstractions |
ILogger interface |
Quick.Core.Caching.Abstractions |
IMemoryCache / ICache interfaces |
Quick.Core.Mapping.Abstractions |
IMapper interface |
Quick.Core.Localization.Abstractions |
IStringLocalization interface |
Quick.Core.MessageQueue.Abstractions |
IMessageQueue<T> · TMessageQueueTask<T> |
Quick.Core.Entity.Repository |
IRepository<T> · TEntityRepository<T> |
| Unit | Description |
|---|---|
Quick.Core.DependencyInjection |
TServiceCollection · TActivatorDelegate<T> · IOptions<T> · IServiceScope |
Quick.Core.AutoMapper |
TAutoMapper · TProfileMap |
Quick.Core.Entity |
TEntity · TDBContext · TDBSet<T> · TEntitySD · TEntityRV |
Quick.Core.Entity.Migrations |
TMigration · TMigrationsManager |
Quick.Core.Entity.UnitOfWork |
IUnitOfWork · TUnitOfWork |
Quick.Core.Logging |
TLoggerBuilder |
Quick.Core.Identity |
TIdentityOptions · TIdentityDbContext<TUser,TRole> |
Quick.Core.Security.UserManager |
IUserManager<TUser,TRole> |
Quick.Core.Security.Jwt |
IJwtTokenService · TJwtOptions |
Quick.Core.TaskControl |
ITaskControl · TBackgroundTasks · TScheduledTasks |
| Unit | Description |
|---|---|
Quick.Core.Mvc |
TMvcServer · TStartupMvc · middleware chain |
Quick.Core.Mvc.Controller |
THttpController · routing/auth attributes |
Quick.Core.Mvc.ActionResult |
IActionResult · Ok · Json · NotFound etc. |
Quick.Core.Mvc.ActionInvoker |
Parameter binding · model validation |
Quick.Core.Mvc.ModelValidation |
[Required] · [Range] · [StringLength] · TModelState |
Quick.Core.Mvc.Filters |
TActionFilterAttribute |
Quick.Core.Mvc.Middleware.Cors |
TCorsMiddleware |
Quick.Core.Mvc.Middleware.Swagger |
TSwaggerMiddleware · OpenAPI 3.0 |
Quick.Core.Mvc.Middleware.RateLimit |
TRateLimitMiddleware |
Quick.Core.Mvc.Middleware.Compression |
Gzip / Deflate response compression |
Quick.Core.Mvc.Middleware.ExceptionHandler |
Global 500 handler |
Quick.Core.Mvc.Middleware.Session |
TSessionMiddleware · TInMemorySessionStore |
Quick.Core.Mvc.ViewEngine.Mustache |
Mustache templates · layouts · partials |
| Unit | Description |
|---|---|
Quick.Core.Extensions.Entity |
AddDBContext<T> |
Quick.Core.Extensions.Authentication |
AddAuthentication · AddIdentity<TUser,TRole> |
Quick.Core.Extensions.Authentication.Jwt |
AddJwtBearer |
Quick.Core.Extensions.Authentication.ApiKey |
AddApiKey · UseMemoryStore |
Quick.Core.Extensions.Authentication.Cookie |
AddCookie |
Quick.Core.Extensions.Authentication.OAuth2 |
AddOpenIdConnect |
Quick.Core.Extensions.Authorization |
AddAuthorization |
Quick.Core.Extensions.AutoMapper |
AddAutoMapper |
Quick.Core.Extensions.Caching.Memory |
AddMemoryCache |
Quick.Core.Extensions.Caching.Redis |
AddRedisCache |
Quick.Core.Extensions.Localization |
AddLocalization |
Quick.Core.Extensions.Hosting |
AddHostedService<T> |
Quick.Core.Extensions.HealthChecks |
AddHealthChecks |
Quick.Core.Extensions.MessageQueue.Redis |
AddRedisMessageQueue<T> |
Quick.Core.Extensions.Compression |
AddResponseCompression |
Quick.Core.Extensions.Serialization |
AddSerialization |
Do you want to learn Delphi or improve your skills? learndelphi.org
Last updated: April 2026 — QuickCore v2.0 · 85 tests passing · 24 samples

