Photo by Sebastian Sørensen from Pexels

Among the features of the mORMot framework for Delphi, there are several mechanisms for publishing a self-hosted HTTP service. If you inspect the source code samples and the documentation, you will see various approaches demonstrated, without necessarily realizing the strengths and weaknesses of each. Before attempting to build my own web API using mORMot, I decided to first familiarize myself with the options.

Broadly speaking, there are three ways to do this:

  • The REST ORM
  • Methods-based services
  • Interface-based services

The framework allows these approaches to be used in combination. In fact, based on the class hierarchy, this practice almost seems encouraged. Before I discuss each of the options, however, let me introduce the class, which appears in all of them.

The SQL HTTP server

The class is defined in the unit and plays the role of HTTP server in all the scenarios covered in this article. Despite its name, this is true regardless of whether any actual SQL is involved (or any database, for that matter).

Creating an instance of this class will immediately set up the server, register URIs and start listening for incoming requests, e.g.

HttpServer := TSQLHttpServer.Create('8080', [ServerDB]);

Important: You may find that on modern versions of Windows, the program must be launched with admin rights in order for URI registration to work.

The first parameter, , is a string representing the port to which you wish to bind the server. The server will listen on this port for HTTP requests.

The second parameter, , is an array of instances. Note that you’ll need one of these even if you aren’t using mORMot for its ORM functionality. I’ve already discussed this class in my article The ORM in mORMot, and once you know how to use both it and , you have everything you need for Option #1 (see below).

For now, know that the , or more specifically its associated instance, determines the root for your URI endpoints, e.g.

http://localhost:8080/root/book/1

This field, which can be set using either the property or a constructor parameter, has a default value of “root”, and accepts any valid URI string.

Furthermore, you probably want to set the property to allow requests from other domains via cross-origin resource sharing (CORS):

HttpServer.AccessControlAllowOrigin := '*';

If your server doesn’t have a database, the class is a viable replacement for .

Option #1: The REST ORM

Let’s take a moment to consider that the mORMot framework implements REST at an ORM level instead of an API level. The nice thing about this is that the REST implementation is not tied to any particular protocol. Unfortunately, in the case of mORMot, it also means that when you publish this ORM as a RESTful HTTP web API, the result is a very thin database wrapper (which might be the reason Synopse advises against exposing such an API to the public).

To publish your ORM, simply pass the instance you’re already using in the array parameter of the constructor. The API will expose CRUD endpoints for each -derived class in the associated model, with URIs determined by the class name.

For example, if your REST server hosts objects, you can retrieve a list of them by issuing a request to a URL like the following:

http://localhost:8080/root/fruit

This will return a JSON array of objects, rendering only their IDs, e.g.

[{"ID":1},{"ID":2},{"ID":3}]

To include fields other than the ID in the response, specify the field names in the select query string parameter as a comma-delimited list, e.g.

http://localhost:8080/root/fruit?select=id,name

These fields will appear in the JSON response, e.g.

[{"ID":1,"Name":"Apple"},{"ID":2,"Name":"Banana"},{"ID":3,"Name":"Orange"}]

To filter the records, you can add conditions in the where query string parameter, e.g.

http://localhost:8080/root/fruit?where=name="Banana"

You can also sort the records by specifying field names in the sort query string parameter, e.g.

http://localhost:8080/root/fruit?sort=name

Note: Using the names of non-existent fields in the query string will result in a 404 error response.

An individual record can be retrieved by appending its ID to the URI, e.g.

http://localhost:8080/root/fruit/2

You can also submit , and requests, which behave as you would expect from a RESTful API.

A property on the class, , can be used to toggle between standard “expanded” JSON responses and mORMot’s own “not expanded” format, which more closely resembles an SQL result set (standard JSON is the default). One quirk I have found is that, in the case of empty result sets, the server will use the non-standard format regardless of this setting (judging by comments in the unit, this behaviour is intentional).

The obvious advantage of this approach is that you can have a fully functional, JSON-speaking RESTful API for only a few lines of code. On the other hand, the resulting API is not very open to customization, and there is no convenient place for business logic to live. I suppose you could still write validation logic in your -derived domain classes, but that hardly seems fulfilling.

Option #2: Publishing a methods-based service

In the second approach, you create a subclass of and extend it with published methods. These methods become available as endpoints in your API and allow you to directly control the HTTP response while giving you access to things like the request path and headers.

When this approach is combined with the first, these methods can be invoked “on” a resource in your existing REST ORM (see further down).

Warning: By using the same instance you’re using for your ORM, you are by default using Option #1 as well. Consider carefully whether this is your intention.

To create a methods-based service, create a new class that inherits from the class, and declare a method, e.g.

TMethodServer = class(TSQLRestServerDB)
published
procedure Eat(Ctxt: TSQLRestServerURIContext);
end;

The parameter list for your method must match the one above exactly.

The parameter gives you access to information about the HTTP request, such as the HTTP verb (). At the same time, this object is also used to define information about the response. The method allows you to return an array of values, for example:

Ctxt.Results(['Yum!']);

You can call this method by issuing e.g. a request to a URL like this:

http://localhost:8080/root/eat

In this case, a JSON response is returned, e.g.

{"result":["Yum!"]}

When the method lives on a instance that also publishes a REST ORM, you can call the method in the context of a specific table, e.g.

http://localhost:8080/root/fruit/eat

You can also include an ID in the request path, e.g.

http://localhost:8080/root/fruit/5/eat

The table name (e.g. “Fruit”) and ID (e.g. 5) can then be obtained from the parameter as and respectively.

For more information about methods-based services, refer to the Client-Server services via methods section in the mORMot design document.

This approach seems very much tailored to the extension of an existing REST ORM server. It allows a lot of versatility with regard to handling the request, which is most welcome, but the structure is not well organised (especially without the context of an ORM). Both in the URI schema and the class itself, methods basically live in a long, flat list.

Option #3: Publishing an interface-based service

Interface-based services allow RPC-like functionality that is closer to WCF services in the .NET world than to a real RESTful web API. When you register an interface and the type of a class that implements it, mORMot publishes all of its methods as HTTP endpoints. Parameters and return types are automatically converted from and to JSON.

Warning: Once again, if you register the service on the instance you’re using for your ORM, you’re also using Option #1.

To begin, define an interface for your service. Be sure to inherit from so as to have run-time type information (RTTI), and do give your interface a GUID (the classic Delphi keystroke + + ).

For example:

IGreetingService = interface(IInvokable)
['{CB6CBA42-7236-4E89-4755-2428DABF34FB}']
function Hello(Name: RawUTF8): RawUTF8;
end;

It’s a good idea to use mORMot’s type instead of for the sake of compatibility. Next, create a class that implements this interface, e.g.

TGreetingService = class(TInterfacedObject, IGreetingService)
protected
function Hello(Name: RawUTF8): RawUTF8;
end;
implementationfunction TGreetingService.Hello(Name: RawUTF8): RawUTF8;
var
Greeting: string;
begin
Greeting := 'Hello!';
if Name <> '' then
begin
Greeting := Format('Hello, %s!', [Name]);
end;
Result := Greeting;
end;

Now, register the service on your instance by calling the method, passing in the type of the concrete class, and an array containing the of the service interface, e.g.

ServerDB.ServiceRegister(TGreetingService, [TypeInfo(IGreetingService)]);

The HTTP server exposes an endpoint for each of the service’s methods, at a URI determined that follows the pattern , e.g.

http://localhost:8080/root/greetingservice/hello?name=Moto

The root is, as usual, determined by the property of your instance, while the service name is derived from the name of the interface (sans the -prefix).

You can call this endpoint with or requests — each results in the function being called on the concrete class and the rendered as JSON. (In the case of a procedure, the response is an empty ). For example:

{"result":["Hello, Moto!"]}

In fact, the same goes for , and even or requests. Parameters are mapped from the query string, but mORMot also checks the request body for JSON. This means you can call the endpoint without a query string, e.g.

http://localhost:8080/root/greetingservice/hello

As long as you supply the arguments in the request body, the end result will be the same, e.g.

{
"name": "Moto"
}

Returning primitive types is OK, but if you want to include objects in the HTTP response, output parameters are the way to go. The mORMot framework has its own set of rules regarding this, and it’s worth going over the documentation to review them. There’s also an article I wrote on some of the framework’s JSON serialization capabilities, which you might find relevant.

Although interface-based services do not provide a parameter like methods-based services do, you can still access low-level information about the HTTP request via the named (declared in the unit).

There are also two routing options available, REST and JSON RPC, which you choose by setting the property of the class to either or . According to the documentation, you can apply custom routing by inheriting your own class from . Both of the examples rely on internal members of mORMot classes, however — a luxury that isn’t available to third-party derivatives.

For more information about interface-based services, refer to the Client-Server services via interfaces section in the mORMot design document.

While not particularly RESTful, interface-based services seem like a good fit for other kinds of service-oriented architectures. They are cleaner to work with than methods-based services, and still allow a degree of flexibility.

Conclusion

Publishing a REST ORM is the easiest way to host a web API using mORMot, and produces the closest thing to a RESTful API that the framework provides out-of-the-box. On the flip side, this option also affords the developer the least amount of control over the end result.

At the other end of the spectrum, there are methods-based services, which, though they seem to exist primarily as a way to extend an existing REST ORM, offer lower-level access to the HTTP context. This approach, again, isn’t as clean as using interface-based services, which lets you define the contract for your API using plain Delphi interfaces.

There’s a lot here to play with, and it bears some experimentation to find the option that best suits your design.

Sample source code

I have created examples for each of the approaches discussed above in the following public repository on GitHub:

Used for this article

  • Delphi 10.3.1 Starter
  • mORMot 1.18

Software developer walking the edge between legacy systems and modern technology. I also make music: https://soundcloud.com/stephanbester