Complete walkthrough of how requests flow through the Delphi 12 + TMS XData + UniDAC + PostgreSQL stack.
The stack consists of these layers:
┌─────────────────────────────────────────┐
│ TMS WEB Core Browser Application │
│ (TAPIDataset, TXDataWebConnection) │
└──────────────────┬──────────────────────┘
│ HTTPS / JSON
┌──────────────────▼──────────────────────┐
│ TMS XData Server (Sparkle HTTP) │
│ Service Operations + Entity Sets │
└──────────────────┬──────────────────────┘
│ UniDAC
┌──────────────────▼──────────────────────┐
│ PostgreSQL Database │
│ Stored Procedures (schema.function) │
└─────────────────────────────────────────┘
https://api.example.com/hydrology/dashboard.getcards
dashboard.getcards → IHydrologyService.GetCards
SELECT * FROM dashboard.getcards(fkuserid_ := :p1)
TYPE
[ServiceContract]
IHydrologyService = INTERFACE(IInvokable)
['{GUID}']
[HttpGet]
FUNCTION GetCards(UserID: Integer): TStream;
[HttpPost]
FUNCTION SaveCard(Card: TCardDTO): TStream;
END;
TYPE
THydrologyService = CLASS(TInterfacedObject, IHydrologyService)
PRIVATE
FConnectionPool: IDBConnectionPool; /// Spring4D injected
PUBLIC
FUNCTION GetCards(UserID: Integer): TStream;
END;
FUNCTION THydrologyService.GetCards(UserID: Integer): TStream;
VAR
Connection: IDBConnection;
Query: TUniQuery;
BEGIN
Connection := FConnectionPool.GetConnection;
Query := TUniQuery.Create(NIL);
TRY
Query.Connection := Connection.Connection;
Query.SQL.Text := 'SELECT * FROM dashboard.getcards(fkuserid_ := :userid)';
Query.ParamByName('userid').AsInteger := UserID;
Query.Open;
Result := QueryToJSONStream(Query);
FINALLY
Query.Free;
END;
END;
/// In server initialization
XDataServer.RegisterService(TypeInfo(IHydrologyService),
THydrologyService, 'hydrology');
/// Design time or code
XDataConnection.URL := 'https://api.example.com/hydrology';
XDataConnection.Connected := True;
/// Design time properties
dsCards.Connection := XDataConnection;
dsCards.EntitySetName := 'dashboard.getcards';
/// Code: Set parameters and load
dsCards.ParamByName('fkuserid_').AsInteger := CurrentUserID;
dsCards.Load(
PROCEDURE
BEGIN
/// Data available here
END);
/// EntitySetName maps to URL path:
/// 'dashboard.getcards' → GET /hydrology/dashboard.getcards?fkuserid_=123
/// For complex params, uses POST with JSON body
Spring4D manages database connection pooling on the server:
/// In DI container setup
Container.RegisterType<IDBConnectionPool, TUniDACConnectionPool>
.AsSingleton
.DelegateTo(
FUNCTION: IDBConnectionPool
BEGIN
Result := TUniDACConnectionPool.Create;
Result.MinPoolSize := 5;
Result.MaxPoolSize := 50;
Result.ConnectionString := GetConnectionString;
END);
/// Connection borrowed from pool
Connection := Pool.GetConnection;
TRY
/// Use connection for queries
FINALLY
Connection := NIL; /// Returns to pool (reference counted)
END;
/// XData converts exceptions to HTTP errors
TRY
Query.Open;
EXCEPT
ON E: Exception DO
RAISE EXDataHttpException.Create(500, E.Message);
END;
dsCards.OnLoadError :=
PROCEDURE(Dataset: TDataset; ErrorMsg: STRING)
BEGIN
ShowMessage('Load failed: ' + ErrorMsg);
END;
/// Or with callback
dsCards.Load(
PROCEDURE
BEGIN
/// Success
END,
PROCEDURE(ErrorMsg: STRING)
BEGIN
/// Failure
Console.log('Error: ' + ErrorMsg);
END);
| Code | Meaning | Typical Cause |
|---|---|---|
| 200 | OK | Success |
| 400 | Bad Request | Invalid parameters |
| 401 | Unauthorized | Missing/invalid JWT token |
| 404 | Not Found | Unknown service/entity |
| 500 | Server Error | Database or code exception |