mormot2-rest-soa
mORMot 2 REST and Service-Oriented Architecture layer: in-process and remote TRestServer descendants, method-based services on TRestServerUriContext, interface-based services declared as IInvokable descendants, and the matching TRestClientUri consumers. This skill is authoritative for the mormot.rest.* namespace and for service registration verbs (ServiceRegister, ServiceDefine). It assumes the conventions defined in mormot2-core (RawUtf8, RTTI) and mormot2-orm (TOrm, TOrmModel). Sibling skills handle adjacent concerns: mormot2-net covers the HTTP/WebSocket transport that wraps the REST server, mormot2-auth-security covers JWT, ECC, and AES primitives, and mormot2-db covers raw SQL.
When to use
- Standing up an in-process
TRestServerDB (SQLite3 backend) or TRestServerFullMemory (no SQLite) to expose a TOrmModel.
- Adding a method-based service: a
published procedure DoSomething(Ctxt: TRestServerUriContext); on a TRestServer descendant.
- Declaring an interface-based service:
IMyService = interface(IInvokable) ['{GUID}'] ... end; plus a TInterfacedObject implementation.
- Registering services on the server with
ServiceRegister / ServiceDefine, picking a lifetime mode (sicShared, sicSingle, sicPerSession, sicPerThread, sicClientDriven, sicPerUser, sicPerGroup).
- Wiring a
TRestClientUri (or TRestClientDB for in-process tests) and calling Client.ServiceDefine([IMyService], sicShared) so the same Pascal interface drives the round trip.
- Using
Ctxt.Session, Ctxt.SessionID, and Ctxt.SessionUser inside a method-based service, or hooking session creation/teardown.
When NOT to use
- Async HTTP, WebSockets, OpenAPI generation, TLS, or HTTP-engine tuning (
useHttpAsync, useBidirAsync, useHttpApi). Use mormot2-net.
- JWT signing, ECC key pairs, AES, OpenSSL bindings, password hashing primitives. Use mormot2-auth-security.
- Defining the underlying
TOrm classes, mapping field attributes, building the TOrmModel, registering virtual tables. Use mormot2-orm.
- Raw SQL,
TSqlDBConnectionProperties, PostgreSQL/MSSQL/MongoDB providers. Use mormot2-db.
- Foundational types (
RawUtf8, TDocVariant), conditional defines, custom record RTTI. Use mormot2-core.
Core idioms
1. Minimal in-process REST server with SQLite3 backend
TRestServerDB is the canonical SQLite-backed REST server. TRestServerFullMemory is the SQLite-free in-memory variant. Both bind to a TOrmModel built per mormot2-orm.
uses
mormot.core.base,
mormot.orm.core,
mormot.rest.sqlite3;
var
Model: TOrmModel;
Server: TRestServerDB;
begin
Model := TOrmModel.Create([TOrmUser, TOrmRole]);
Server := TRestServerDB.Create(Model, 'data.db3');
try
Server.Server.CreateMissingTables;
// Server.Orm now exposes the model; HTTP transport comes from mormot2-net.
finally
Server.Free;
Model.Free; // after the TRest, never before
end;
end;
2. Method-based service
A published procedure on a TRestServer descendant becomes a URI under ModelRoot/MethodName. Signature MUST match TOnRestServerCallBack.
type
TAppServer = class(TRestServerDB)
published
procedure Sum(Ctxt: TRestServerUriContext);
end;
procedure TAppServer.Sum(Ctxt: TRestServerUriContext);
begin
Ctxt.Results([Ctxt.InputDouble['a'] + Ctxt.InputDouble['b']]);
end;
// GET /root/Sum?a=3&b=4 -> {"Result":7}
3. Interface-based service contract
Inherit from IInvokable and embed a GUID. The interface is the contract; the same Pascal unit ships to client and server.
type
TItem = packed record
Sku: RawUtf8;
Price: currency;
end;
TItemArray = array of TItem;
IInventoryService = interface(IInvokable)
['{4C2E1A8B-9D7F-4E3A-9B6C-7E5D1A2B3C4D}']
procedure ListItems(out Items: TItemArray);
function GetCount: integer;
end;
4. Server-side service registration
ServiceDefine is the modern verb (it accepts the interface itself rather than its TypeInfo). Pick the lifetime mode deliberately; sicShared is the safest default for stateless services and is the cheapest in throughput.
uses
mormot.rest.server,
mormot.soa.server;
type
TInventoryService = class(TInterfacedObject, IInventoryService)
public
procedure ListItems(out Items: TItemArray);
function GetCount: integer;
end;
// ... after Server is constructed:
Server.ServiceDefine(TInventoryService, [IInventoryService], sicShared);
5. Client consuming the same interface
The client builds the same TOrmModel, opens a TRestClientUri (HTTP variant comes from mormot2-net), then calls ServiceDefine to wire a fake implementation that serializes calls as JSON.
uses
mormot.rest.client;
var
Service: IInventoryService;
Items: TItemArray;
begin
Client.ServiceDefine([IInventoryService], sicShared);
if Client.Services.Resolve(IInventoryService, Service) then
Service.ListItems(Items);
end;
Common pitfalls
- Forgetting
IInvokable (or its IInvokable-derived ancestor) on the interface. Without it the interface has no RTTI, ServiceDefine cannot inspect parameters, and registration silently does nothing useful at runtime. Always inherit from IInvokable and embed a GUID.
- Forgetting the GUID on the interface declaration. mORMot uses the GUID to identify the contract across the wire. A missing or duplicated GUID makes
Services.Resolve return false or, worse, route to the wrong service.
- Using raw
Boolean parameters in interface methods when nullability matters. Nullable booleans round-trip as 0 / 1 / missing, which is ambiguous. Use variant for nullable values, or split into two methods (SetEnabled / SetDisabled).
- Misjudging the lifetime mode.
sicShared is one instance for ALL calls and threads (you must make the implementation thread-safe). sicSingle is a fresh instance per call (default, safest, slowest). sicPerSession requires authentication. sicPerThread is one per server worker thread. Pick on read/write profile and statefulness, not by feel.
- Defining a method-based handler with the wrong signature. The procedure MUST be
procedure(Ctxt: TRestServerUriContext), declared published, and live on a class derived from TRestServer. Anything else is invisible to the URI router.
- Not setting up CORS for browser clients.
TRestHttpServer (in mormot2-net) has explicit CORS knobs; without them an SPA on a different origin will hit a preflight wall. Configure CORS at the HTTP wrapper layer.
- Mutating shared state inside a
sicShared service without locking. sicShared is single-instance, multi-thread. Use a TSynLocker (see mormot2-core) or restructure to keep the service stateless.
See also
$MORMOT2_DOC_PATH/mORMot2-SAD-Chapter-10.md - JSON / REST
$MORMOT2_DOC_PATH/mORMot2-SAD-Chapter-11.md - Client-Server Architecture
$MORMOT2_DOC_PATH/mORMot2-SAD-Chapter-14.md - Method-based Services
$MORMOT2_DOC_PATH/mORMot2-SAD-Chapter-15.md - Interfaces and SOLID
$MORMOT2_DOC_PATH/mORMot2-SAD-Chapter-16.md - Service-Oriented Architecture
references/rest-server-shapes.md
references/interface-services.md
references/session-and-auth.md
mormot2-core for RawUtf8 conventions and TSynLocker
mormot2-orm for TOrm/TOrmModel definitions
mormot2-net for HTTP/WebSocket/TLS transport
mormot2-auth-security for JWT, ECC, AES primitives
1---2name: mormot2-rest-soa3description: Use when building REST servers/clients, method-based services, or interface-based SOA in mORMot 2. Do NOT use for HTTP transport (use mormot2-net) or auth (use mormot2-auth-security).4---56# mormot2-rest-soa78mORMot 2 REST and Service-Oriented Architecture layer: in-process and remote `TRestServer` descendants, method-based services on `TRestServerUriContext`, interface-based services declared as `IInvokable` descendants, and the matching `TRestClientUri` consumers. This skill is authoritative for the `mormot.rest.*` namespace and for service registration verbs (`ServiceRegister`, `ServiceDefine`). It assumes the conventions defined in `mormot2-core` (RawUtf8, RTTI) and `mormot2-orm` (`TOrm`, `TOrmModel`). Sibling skills handle adjacent concerns: `mormot2-net` covers the HTTP/WebSocket transport that wraps the REST server, `mormot2-auth-security` covers JWT, ECC, and AES primitives, and `mormot2-db` covers raw SQL.910## When to use1112- Standing up an in-process `TRestServerDB` (SQLite3 backend) or `TRestServerFullMemory` (no SQLite) to expose a `TOrmModel`.13- Adding a method-based service: a `published procedure DoSomething(Ctxt: TRestServerUriContext);` on a `TRestServer` descendant.14- Declaring an interface-based service: `IMyService = interface(IInvokable) ['{GUID}'] ... end;` plus a `TInterfacedObject` implementation.15- Registering services on the server with `ServiceRegister` / `ServiceDefine`, picking a lifetime mode (`sicShared`, `sicSingle`, `sicPerSession`, `sicPerThread`, `sicClientDriven`, `sicPerUser`, `sicPerGroup`).16- Wiring a `TRestClientUri` (or `TRestClientDB` for in-process tests) and calling `Client.ServiceDefine([IMyService], sicShared)` so the same Pascal interface drives the round trip.17- Using `Ctxt.Session`, `Ctxt.SessionID`, and `Ctxt.SessionUser` inside a method-based service, or hooking session creation/teardown.1819## When NOT to use2021- Async HTTP, WebSockets, OpenAPI generation, TLS, or HTTP-engine tuning (`useHttpAsync`, `useBidirAsync`, `useHttpApi`). Use **mormot2-net**.22- JWT signing, ECC key pairs, AES, OpenSSL bindings, password hashing primitives. Use **mormot2-auth-security**.23- Defining the underlying `TOrm` classes, mapping field attributes, building the `TOrmModel`, registering virtual tables. Use **mormot2-orm**.24- Raw SQL, `TSqlDBConnectionProperties`, PostgreSQL/MSSQL/MongoDB providers. Use **mormot2-db**.25- Foundational types (`RawUtf8`, `TDocVariant`), conditional defines, custom record RTTI. Use **mormot2-core**.2627## Core idioms2829### 1. Minimal in-process REST server with SQLite3 backend3031`TRestServerDB` is the canonical SQLite-backed REST server. `TRestServerFullMemory` is the SQLite-free in-memory variant. Both bind to a `TOrmModel` built per `mormot2-orm`.3233```pascal34uses35 mormot.core.base,36 mormot.orm.core,37 mormot.rest.sqlite3;3839var40 Model: TOrmModel;41 Server: TRestServerDB;42begin43 Model := TOrmModel.Create([TOrmUser, TOrmRole]);44 Server := TRestServerDB.Create(Model, 'data.db3');45 try46 Server.Server.CreateMissingTables;47 // Server.Orm now exposes the model; HTTP transport comes from mormot2-net.48 finally49 Server.Free;50 Model.Free; // after the TRest, never before51 end;52end;53```5455### 2. Method-based service5657A `published` procedure on a `TRestServer` descendant becomes a URI under `ModelRoot/MethodName`. Signature MUST match `TOnRestServerCallBack`.5859```pascal60type61 TAppServer = class(TRestServerDB)62 published63 procedure Sum(Ctxt: TRestServerUriContext);64 end;6566procedure TAppServer.Sum(Ctxt: TRestServerUriContext);67begin68 Ctxt.Results([Ctxt.InputDouble['a'] + Ctxt.InputDouble['b']]);69end;7071// GET /root/Sum?a=3&b=4 -> {"Result":7}72```7374### 3. Interface-based service contract7576Inherit from `IInvokable` and embed a GUID. The interface is the contract; the same Pascal unit ships to client and server.7778```pascal79type80 TItem = packed record81 Sku: RawUtf8;82 Price: currency;83 end;84 TItemArray = array of TItem;8586 IInventoryService = interface(IInvokable)87 ['{4C2E1A8B-9D7F-4E3A-9B6C-7E5D1A2B3C4D}']88 procedure ListItems(out Items: TItemArray);89 function GetCount: integer;90 end;91```9293### 4. Server-side service registration9495`ServiceDefine` is the modern verb (it accepts the interface itself rather than its `TypeInfo`). Pick the lifetime mode deliberately; `sicShared` is the safest default for stateless services and is the cheapest in throughput.9697```pascal98uses99 mormot.rest.server,100 mormot.soa.server;101102type103 TInventoryService = class(TInterfacedObject, IInventoryService)104 public105 procedure ListItems(out Items: TItemArray);106 function GetCount: integer;107 end;108109// ... after Server is constructed:110Server.ServiceDefine(TInventoryService, [IInventoryService], sicShared);111```112113### 5. Client consuming the same interface114115The client builds the same `TOrmModel`, opens a `TRestClientUri` (HTTP variant comes from `mormot2-net`), then calls `ServiceDefine` to wire a fake implementation that serializes calls as JSON.116117```pascal118uses119 mormot.rest.client;120121var122 Service: IInventoryService;123 Items: TItemArray;124begin125 Client.ServiceDefine([IInventoryService], sicShared);126 if Client.Services.Resolve(IInventoryService, Service) then127 Service.ListItems(Items);128end;129```130131## Common pitfalls132133- **Forgetting `IInvokable` (or its `IInvokable`-derived ancestor) on the interface.** Without it the interface has no RTTI, `ServiceDefine` cannot inspect parameters, and registration silently does nothing useful at runtime. Always inherit from `IInvokable` and embed a GUID.134- **Forgetting the GUID on the interface declaration.** mORMot uses the GUID to identify the contract across the wire. A missing or duplicated GUID makes `Services.Resolve` return false or, worse, route to the wrong service.135- **Using raw `Boolean` parameters in interface methods when nullability matters.** Nullable booleans round-trip as `0` / `1` / missing, which is ambiguous. Use `variant` for nullable values, or split into two methods (`SetEnabled` / `SetDisabled`).136- **Misjudging the lifetime mode.** `sicShared` is one instance for ALL calls and threads (you must make the implementation thread-safe). `sicSingle` is a fresh instance per call (default, safest, slowest). `sicPerSession` requires authentication. `sicPerThread` is one per server worker thread. Pick on read/write profile and statefulness, not by feel.137- **Defining a method-based handler with the wrong signature.** The procedure MUST be `procedure(Ctxt: TRestServerUriContext)`, declared `published`, and live on a class derived from `TRestServer`. Anything else is invisible to the URI router.138- **Not setting up CORS for browser clients.** `TRestHttpServer` (in `mormot2-net`) has explicit CORS knobs; without them an SPA on a different origin will hit a preflight wall. Configure CORS at the HTTP wrapper layer.139- **Mutating shared state inside a `sicShared` service without locking.** `sicShared` is single-instance, multi-thread. Use a `TSynLocker` (see `mormot2-core`) or restructure to keep the service stateless.140141## See also142143- `$MORMOT2_DOC_PATH/mORMot2-SAD-Chapter-10.md` - JSON / REST144- `$MORMOT2_DOC_PATH/mORMot2-SAD-Chapter-11.md` - Client-Server Architecture145- `$MORMOT2_DOC_PATH/mORMot2-SAD-Chapter-14.md` - Method-based Services146- `$MORMOT2_DOC_PATH/mORMot2-SAD-Chapter-15.md` - Interfaces and SOLID147- `$MORMOT2_DOC_PATH/mORMot2-SAD-Chapter-16.md` - Service-Oriented Architecture148- `references/rest-server-shapes.md`149- `references/interface-services.md`150- `references/session-and-auth.md`151- `mormot2-core` for RawUtf8 conventions and TSynLocker152- `mormot2-orm` for TOrm/TOrmModel definitions153- `mormot2-net` for HTTP/WebSocket/TLS transport154- `mormot2-auth-security` for JWT, ECC, AES primitives