Catalog · How the catalog works
DelphiAPIStarterKit
DelphiAPIStarterKit is a clean and reusable starter template for building RESTful APIs and web services with Delphi WebBroker. It provides a structured project foundation, routing, request handling, response helpers, and practical examples to speed up backend development.
- ★ 4 on GitHub
Anyone may press Interesting, signed in or not. It helps others find this project.
README
DelphiAPIStarterKit
English | Bahasa Indonesia
DelphiAPIStarterKit is a Delphi WebBroker starter template for building REST APIs and web services with a clean backend structure: RTTI-based routing, response helpers, request validation, service layer, repository layer, FireDAC connection factory, and practical CRUD modules.
This project is intended as a reusable foundation for Delphi backend APIs. It is not a production-ready deployment template without additional configuration and security review.
Features
- Delphi WebBroker API server.
- RTTI-based route dispatch from URL resources to registered endpoint classes.
- FireDAC database access for MySQL/MariaDB.
- Module structure based on
RestAPI,Service,Repository,Validator, andDTOunits. - Standard JSON response envelope.
- Authentication example with sessions and access tokens.
- Example modules:
- Auth
- Users
- Category
- Product
- Customer
- Sample database schema in
assets/databases/demo_delphirest.sql. - API documentation and Postman collection are available under
docs/apiif the docs folder is published.
Requirements
- Delphi with WebBroker, FireDAC, FireDAC MySQL driver, and the standard Indy/WebBroker bridge units.
- IPPeer runtime units if your Delphi installation separates these dependencies.
- MySQL or MariaDB server.
- MySQL/MariaDB native client library matching the application target architecture.
- Windows for the default
Win32build target. - Linux64 is enabled in the Delphi project and requires the Delphi Linux toolchain/PAServer setup.
The default build script targets Debug | Win32. Use environment variables to change the Delphi environment script, build configuration, or platform.
Project Structure
sources/
app/ WebBroker module and server bootstrap
core/ Core routing, response, request, constants, config
infrastructure/
database/ FireDAC connection factory and query helper
security/ Token/security helper
modules/
auth/ Auth endpoint, service, repository, validator, DTO
users/ User endpoint, service, repository, validator, DTO
category/ Category endpoint, service, repository, validator, DTO
products/ Product endpoint, service, repository, validator, DTO
customers/ Customer endpoint, service, repository, validator, DTO
shared/
helpers/ Reusable helpers
assets/
databases/
demo_delphirest.sql MySQL/MariaDB sample schemaJSON Response Format
API responses use this envelope:
{
"status": 200,
"messages": "OK",
"servertime": "1780962999",
"data": []
}For error responses, status follows the HTTP status code and data is returned as an array containing an empty object.
Database Setup
The database connection is not stored in source code. Configure it before running the server.
Preferred option: set environment variables:
setx DELPHI_API_DB_SERVER "localhost"
setx DELPHI_API_DB_DATABASE "demo_delphirest"
setx DELPHI_API_DB_USER "root"
setx DELPHI_API_DB_PASSWORD ""Alternative option: create config.ini in the application base directory:
[Database]
Server=localhost
Database=demo_delphirest
User_Name=root
Password=
CharacterSet=utf8mb4
POOL_MaximumItems=50
POOL_ExpireTimeout=300000Use config.example.ini as the template. Do not commit your real config.ini.
Database and security settings share one configuration resolver. Set DELPHI_API_CONFIG to an absolute existing file path, or place config.ini beside the executable. Without an explicit path, the startup working directory is a compatibility fallback when the adjacent file does not exist. An invalid explicit path stops startup, including when environment overrides are present. Individual setting environment variables take precedence over file values.
The HMAC secret is validated and cached before the listener starts. Restart the process after changing configuration or the secret; token/password hashing algorithms remain unchanged. Without an explicit configuration path or environment secret, a nonblank legacy Linux secret from /var/lib/<executable-name>/config.ini keeps priority over the default main file, preserving existing token/password keys. Use DELPHI_API_CONFIG or the secret environment variable to deliberately select the new source. New deployments should use the shared configuration file or environment variables.
HTTP gzip Compression
HTTP JSON compression defaults to the following startup settings in the same config.ini:
[HTTP]
GzipEnabled=True
GzipMinimumBytes=1024
GzipLevel=1Gzip is used only when accepted by the client and the UTF-8 body reaches the minimum size (1024 bytes = 1 KiB). Levels 1-9 trade CPU time for compression ratio; level 1 favors speed. Smaller responses and output that would grow remain uncompressed. Settings are read once and require a restart. See gzip benchmark instructions.
Import Schema via MySQL CLI
Create the database:
mysql -u root -p -e "CREATE DATABASE demo_delphirest CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"Import the schema:
mysql -u root -p demo_delphirest < assets\databases\demo_delphirest.sqlIf PowerShell causes redirect issues, run the command through cmd:
cmd /c "mysql -u root -p demo_delphirest < assets\databases\demo_delphirest.sql"Import Schema via phpMyAdmin
- Open phpMyAdmin.
- Create a database named
demo_delphirest. - Select the database.
- Open the
Importtab. - Select
assets/databases/demo_delphirest.sql. - Run the import.
MySQL Compatibility Notes
- The sample schema targets MySQL/MariaDB with InnoDB and
utf8mb4. - During setup, the database account needs permission to create/import tables.
- At runtime, use a dedicated application account with only the required CRUD permissions for the application database.
- The schema file creates tables only. If you need test login data, insert a demo role and user that match your configured
DELPHI_API_HMAC_SECRET.
Application Secret Setup
The HMAC signature secret is not stored in source code. Configure it before using login, password hashing, token creation, or token validation.
Preferred option: set an environment variable:
setx DELPHI_API_HMAC_SECRET "replace-with-a-long-random-secret"For the current terminal session only:
set DELPHI_API_HMAC_SECRET=replace-with-a-long-random-secretAlternative option: create config.ini in the application base directory:
[Security]
HMACSecret=replace-with-a-long-random-secretUse config.example.ini as the template. Do not commit your real config.ini.
MySQL Client Library
FireDAC MySQL requires a native client library that matches the application bitness:
- A
Win32application requires a 32-bit client library. - A
Win64application requires a 64-bit client library. - A
Linux64deployment requires a compatible 64-bitlibmysqlclient.soorlibmariadb.soavailable on the Linux server.
Do not download libmysql.dll from unofficial DLL mirrors. Use one of these official sources:
- MySQL C API / libmysqlclient: https://dev.mysql.com/downloads/c-api/
- MySQL Connector/C++ package: https://dev.mysql.com/downloads/connector/cpp/
- MariaDB Connector/C: https://mariadb.com/docs/connectors/mariadb-connector-c
MariaDB Connector/C can connect to MySQL and MariaDB, and MariaDB documents the C connector as LGPLv2.1 licensed.
Adding libmysql.dll
Simple setup:
- Download the MySQL/MariaDB client library from an official source.
- Choose the same architecture as your Delphi build target.
- Extract
libmysql.dllfrom the package. - Place
libmysql.dllin the same folder as the executable, for example:
bin/libmysql.dllAlternatively, place the DLL directory in the Windows PATH.
If you run from the IDE and the current directory is different, make sure VendorHome points to the directory containing the DLL. The current Windows startup code sets:
DM.FDPhysMySQLDriverLink.VendorHome := ExtractFilePath(ExpandFileName(ParamStr(0)));Windows defaults to the executable directory. Override [Database] VendorHome / VendorLib or DELPHI_API_DB_VENDOR_HOME / DELPHI_API_DB_VENDOR_LIB for a different native client location. Use absolute deployment paths; environment values take precedence.
For an open-source repository, the recommended approach is not to commit libmysql.dll or binary ZIP files. Document the dependency and let users install the client library from the official vendor package.
Windows SSL Errors with MySQL 8
If FireDAC reports SSL connection error: unknown error number, check the version of the client DLL actually loaded. Older clients built with yaSSL only support TLS 1.0/1.1 and can fail against servers that only accept TLS 1.2/1.3. require_secure_transport=OFF does not prevent clients from attempting TLS connections.
Use an official x64 client from the MySQL 8.4 Windows ZIP package, including its matching OpenSSL dependencies. For Win64 deployment with bin as the current directory and the VendorHome setting above, the following layout has been tested:
bin/DelphiAPIStarterKit.exe
bin/config.ini
bin/lib/libmysql.dll
bin/libcrypto-3-x64.dll
bin/libssl-3-x64.dllStop the application before replacing libraries and back up the old DLL. MySQL 8.4 x64 cannot be used with Win32 builds. Keep the server's TLS protocol settings when resolving old client compatibility issues.
Linux Client Library Notes
For Linux64 deployment, install the MySQL/MariaDB client library on the target server using the server distribution package manager or the official vendor package.
Example native library override in the selected config.ini:
[Database]
VendorLib=/usr/lib/x86_64-linux-gnu/libmysqlclient.so.21Linux preserves /usr/lib/x86_64-linux-gnu/libmysqlclient.so.21 as the default when that file exists; otherwise FireDAC uses its normal library lookup. Configure [Database] VendorHome / VendorLib or the corresponding environment variables for other distributions and client layouts. The startup message confirms the listener is active; it does not prove database connectivity.
Build
Default build script:
compile.batThe script:
- Stops
DelphiAPIStarterKit.exeif it is already running. - Calls
rsvars.batfromDELPHI_RSVARS, or from%BDS%\bin\rsvars.batwhen running inside a Delphi command prompt. - Builds the project via MSBuild.
- Defaults to
Debug | Win32.
When running from a normal terminal, set DELPHI_RSVARS first:
set DELPHI_RSVARS=C:\Program Files (x86)\Embarcadero\Studio\37.0\bin\rsvars.bat
compile.batOptional build overrides:
set BUILD_CONFIG=Release
set BUILD_PLATFORM=Win64
compile.batLinux64 build example:
set BUILD_CONFIG=Release
set BUILD_PLATFORM=Linux64
compile.batLinux builds require a configured Delphi Linux toolchain and PAServer connection.
Manual build:
msbuild DelphiAPIStarterKit.dproj /t:Make /p:Config=Debug /p:Platform=Win32 /nologo /v:minimalRun
After building, run the executable from the output folder. Make sure:
- MySQL/MariaDB server is running.
- The
demo_delphirestdatabase exists and the schema has been imported. - The MySQL client DLL is available to FireDAC.
- The server port is not already used by another application.
Default local base URL:
http://localhost:9381k6 Read Benchmark
On 31 October 2026, the Win64 Release build was tested with k6 and the API on the same Windows host; MySQL 8.4.11 ran on a separate host over the network. The test used 30 fixed virtual users for 60 seconds, plus graceful completion, against eight GET list/detail endpoints for User, Product, Category, and Customer. The preflight returned 1 user, 8 products, 5 categories, and 100 customers.
| Metric | Result |
|---|---|
| Requests, including preflight | 13,992 |
| Combined throughput | 229.82 requests/s |
| Failed requests | 0% |
| Response checks passed | 118,932 / 118,932 |
| HTTP average / p95 / p99 | 53.13 / 165.77 / 276.77 ms |
| Maximum HTTP duration | 654.87 ms |
| Completed / interrupted iterations | 1,748 / 0 |
All thresholds passed, including p95 below 500 ms for every endpoint. Customer list was the slowest, with 135.05 ms average and 300.27 ms p95. The load phase sent 1,748 requests to each endpoint. Global metrics include preflight; throughput combines all endpoints and reflects the script's pacing. This short read-only test does not establish maximum capacity or long-term stability. Image, authentication workflows, and write operations were excluded; row counts were observed from responses and not independently verified against the database.
See the benchmark guide for the workload, configuration, and interpretation.
API Quickstart
Import the Postman collection:
docs/api/postman.collection.jsonSet the collection variable:
base_url = http://localhost:9381/Login request example:
curl -X POST http://localhost:9381/api/v1/Auth/Login ^
-H "Content-Type: application/json" ^
-d "{\"username\":\"demo_admin\",\"password\":\"demo_admin\",\"device_id\":\"local-dev\",\"device_name\":\"CLI\"}"The database schema does not insert a default demo user. Create your own local test user before expecting the login example to succeed.
All endpoints under User, Product, Category, and Customer require an access token from the login response. Pass the token via the x-api-token header:
x-api-token: <access_token>The server also accepts access-token and Authorization: Bearer <token> as fallback header names.
API Route Pattern
Base route:
/api/v1/{resource}Examples:
POST /api/v1/auth/Login
POST /api/v1/auth/Refresh
POST /api/v1/auth/Logout
GET /api/v1/users
POST /api/v1/users
PUT /api/v1/users/{user_id}
DELETE /api/v1/users/{user_id}
GET /api/v1/category
GET /api/v1/products
GET /api/v1/customersRoutes are resolved to registered endpoint classes using a lightweight RTTI-based dispatcher. The dispatcher builds the target class name from the API version and resource name:
TRestClass{APIVersion}{RequestClass}Example:
/api/v1/users -> TRestClassV1UserInternally, the core dispatcher uses FindClass to locate the registered endpoint class, creates an instance, and invokes its Route method. The endpoint Route method then maps the HTTP method and path to the proper service action.
Adding a New Endpoint
Example: adding an orders resource.
1. Create the Module Folder
sources/modules/orders/2. Create the DTO Unit
Example file:
sources/modules/orders/Order.DTO.pasKeep request and response records explicit:
type
TOrderCreateRequest = record
CustomerID: string;
OrderDate: TDateTime;
Notes: string;
end;3. Create the Validator
Example file:
sources/modules/orders/Order.Validator.pasThe validator reads and validates the TFDMemTable request before business logic runs.
Use existing helpers where possible:
THelperValidator.GetRequiredString(...)
THelperValidator.GetOptionalString(...)
THelperValidator.ParseIntegerField(...)4. Create the Repository
Example file:
sources/modules/orders/Order.Repository.pasRepositories should contain database access only. Use parameterized queries:
TQueryFunction.SQLAdd(LDataset,
'INSERT INTO orders (order_id, customer_internal_id, notes) VALUES (:order_id, :customer_id, :notes)',
True
);
TQueryFunction.SQLParamByName(LDataset, 'order_id', AOrderID);
TQueryFunction.SQLParamByName(LDataset, 'customer_id', ACustomerID);
TQueryFunction.SQLParamByName(LDataset, 'notes', ANotes);
TQueryFunction.ExecSQL(LDataset);Do not concatenate raw user input into SQL.
5. Create the Service
Example file:
sources/modules/orders/Order.Service.pasServices contain business logic, transaction handling, and repository calls.
Write operation pattern:
FConnection.StartTransaction;
try
FRepository.CreateOrder(...);
FConnection.Commit;
except
on E: Exception do begin
THelperTransaction.Rollback(FConnection);
Exit(InternalServerError);
end;
end;6. Create the RestAPI Unit
Example file:
sources/modules/orders/RestAPI.Order.pasThe class must match the route naming pattern:
type
TRestClassV1Order = class(TPersistent)
public
function Route(AConnection: TFDConnection; AData: TFDMemTable;
AWebAction: TWebActionItem; ARequest: TWebRequest;
AResponse: TWebResponse; out AStatusCode: Integer): string;
end;Inside Route, use THelperEndpoint.ExecuteRoute like the existing modules.
7. Register the API Class
Add the new unit to the uses clause in:
sources/core/BFA.Core.Rest.pasThen register the class in RegisterClassAPI. This step is required because the RTTI dispatcher resolves endpoint classes through Delphi's class registry:
RegisterClassAPI([TRestClassV1User, TRestClassV1Auth, TRestClassV1Product,
TRestClassV1Category, TRestClassV1Customer, TRestClassV1Order]);8. Add Units to the Project
Add the new units to:
DelphiAPIStarterKit.dprDelphiAPIStarterKit.dproj
If you use the Delphi IDE, add the units through the Project Manager so the .dproj file is updated.
9. Add the Database Table
Create a migration/schema update in the database assets or migration docs.
Example:
CREATE TABLE `orders` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`order_id` CHAR(36) NOT NULL,
`customer_internal_id` BIGINT UNSIGNED NOT NULL,
`notes` VARCHAR(255) NULL DEFAULT NULL,
`created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` DATETIME NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
`deleted_at` DATETIME NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uq_orders_public_id` (`order_id`),
KEY `idx_orders_customer` (`customer_internal_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;10. Update API Documentation
Add endpoint documentation:
docs/api/orders.mdUpdate the Postman collection if used:
docs/api/postman.collection.jsonCurrent Database Tables
The sample schema contains:
m_roleusersuser_sessionaccess_tokencategoryproductcustomer
Import the schema from:
assets/databases/demo_delphirest.sqlSecurity Notes
Before production use:
- Move database credentials to config/environment variables.
- Configure
DELPHI_API_HMAC_SECRETor[Security] HMACSecretinconfig.ini. - Review the password hashing strategy. The starter currently uses HMAC-SHA256 with an application secret for simplicity; production systems should use a password hashing algorithm such as bcrypt, Argon2, or PBKDF2 with per-user salts and an appropriate work factor.
- Do not expose stack traces, SQL text, tokens, passwords, or secrets in responses/logs.
- Run the API behind HTTPS.
- Restrict CORS to trusted application domains.
- Give the MySQL user only the permissions it needs.
License
The project source code uses the license provided in LICENSE.
Third-party dependencies such as MySQL/MariaDB client libraries follow their vendor licenses and should not be committed directly into this repository.
Contributing
See CONTRIBUTING.md for development setup, coding standards, build validation, and documentation expectations.
Security Policy
See SECURITY.md for vulnerability reporting and security review guidance.
Changelog
See CHANGELOG.md for unreleased changes and release notes.

