Every now and then someone asks me: "Delphi? Is that still a thing?"
It is. It runs in business software, hospitals, factories, in products that have been paying the bills for thirty years. And in 2026 it can do something that many so-called modern stacks still turn into an afternoon of setup: go from an empty folder to a REST server with a database, full CRUD and ready-made configuration, without writing a single line of plumbing code by hand.
Let's see how, step by step.
1. One wizard, five pages
Trysil is an open source ORM for Delphi that I have been maintaining for years. Alongside the library there is the Trysil Expert, an add-in for the Delphi IDE.
Trysil > Create new Trysil API REST opens a wizard:
- project name and folder;
- base URI and port;
- three checkboxes: Multi-tenant, Authorization (login and JWT tokens, RS256-signed too), Log;
- the database, one of seven: SQLite, PostgreSQL, SQL Server, Firebird, InterBase, MariaDB, Oracle;
- the name of the Windows service.
Click Finish: the Expert downloads the template, strips the code of the features you did not pick and opens the project in the IDE. No {$IFDEF} scattered across the sources: only what you asked for is there.
The same executable runs as a Windows service, as a tray application (with /debug) and as a console application on Linux.
2. Design the model
Trysil > Design entity model: entities on the left, columns on the right. I add Customer with CompanyName and City. ID and VersionID (for optimistic locking) are already there and cannot be touched.
The model is saved as one JSON file per entity next to the project, so it goes under version control together with the code.
3. Generate SQL and code
Trysil > Generate DDL script writes the CREATE TABLE for the chosen dialect. If the database already exists, the ALTER checkbox compares the model with a reference database and writes only what is missing: no DROP, no surprises.
Trysil > Generate entity model writes the entity units:
This is the Customer unit:
[TTable('Customers')]
[TSequence('CustomersID')]
TCustomer = class
strict private
[TPrimaryKey]
[TColumn('ID')]
FID: TTPrimaryKey;
[TRequired]
[TMaxLength(100)]
[TColumn('CompanyName')]
FCompanyName: String;
[TMaxLength(50)]
[TColumn('City')]
FCity: String;
[TVersionColumn]
[TColumn('VersionID')]
FVersionID: TTVersion;
public
property ID: TTPrimaryKey read FID;
property CompanyName: String read FCompanyName write FCompanyName;
property City: String read FCity write FCity;
property VersionID: TTVersion read FVersionID;
end;
A plain class with attributes. No base class, no generated code you must not touch. Trysil enforces the validations ([TRequired], [TMaxLength]) before every write.
With the Generate & register API REST controllers checkbox the Expert also writes the controller and registers it in the server. Here it is, in full:
[TUri('/customer')]
TCustomerController = class(TReadWriteController<TCustomer>)
end;
4. F9
Build, run, and from a terminal:
curl -X POST http://localhost:4450/api/customer \
-H "Content-Type: application/json" \
-d '{"companyName":"Acme","city":"Florence"}'
curl http://localhost:4450/api/customer
[{"id":1,"companyName":"Acme","city":"Florence","versionID":1}]
Those three lines of controller expose:
| Method | Route | What it does |
|---|---|---|
| GET | /customer |
all records |
| GET | /customer/{id} |
one record |
| POST | /customer/select |
filtering, sorting, paging |
| GET | /customer/metadata |
entity metadata |
| POST | /customer |
insert with validation |
| PUT | /customer |
update with optimistic locking |
| DELETE | /customer/{id}/{version} |
delete |
If two clients edit the same record, the second one gets an error instead of overwriting the first: that is what VersionID is for.
5. What's underneath
The generated code is not a black box: it is Trysil, and you can use it without the Expert and without HTTP.
LFilter := LContext.CreateFilterBuilder<TCustomer>()
.Where('City').Equal('Florence')
.OrderByAsc('CompanyName')
.Limit(20)
.Build;
LContext.Select<TCustomer>(LCustomers, LFilter);
And the rest, when you need it:
-
soft delete and audit trail with one attribute (
[TDeletedAt],[TCreatedBy], ...); - declarative JOINs with attributes, for read-only entities;
- RawSelect for GROUP BY, UNION and subqueries mapped to DTOs;
-
multi-tenant: one database per tenant, picked from the
Hostheader; - JWT HS256 and RS256, with keys handled as objects;
- every package also compiles for Linux64.
Why I built it
Delphi has a smaller open source ecosystem than other languages, and people working with it often rewrite the same things from scratch: mapping, CRUD, endpoints, configuration. Trysil exists to take that work away, and the Expert to take away what is left.
If you use Delphi, give it a try and tell me what's missing. If you don't, I hope I at least changed your mind about how "old" it is.
- Repo: https://github.com/davidlastrucci/Trysil
- Expert guide: https://davidlastrucci.github.io/Trysil/tooling/expert/
A star on GitHub helps the project get found.



Top comments (0)