Search Results for

    Show / Hide Table of Contents

    Other Tasks

    This chapter explains basic tasks you can do with XData in code. It basically explains how to setup and start the server and how to do some basic configuration using the available classes and interfaces. The following topics describe the most common XData programming tasks and main classes and interfaces.

    Creating an XData Server

    TMS XData Server is based on the TMS Sparkle framework. The actual XData Server is a Sparkle server module that you add to a Sparkle HTTP Server.

    Please refer to the following topics to learn more about TMS Sparkle servers:

    • Overview of TMS Sparkle HTTP Server

    • Creating an HTTP Server to listen for requests

    • TMS Sparkle Server Modules

    To create the XData Server, just create and add a XData Server Module (TXDataServerModule class, declared in unit XData.Server.Module) to the Sparkle HTTP Server. The following code illustrates how to create and run the server. Note that the code that creates the XData server module is not displayed here. You should refer to the "XData Server Module" topic to learn about how to create the module.

    uses
      {...}, 
      Sparkle.HttpSys.Server, XData.Server.Module;
    
    function CreateXDataServerModule(const BaseUrl: string): TXDataServerModule;
    begin
      // Create and return the TXDataServerModule here,
      // using the BaseUrl as the server address
    end;
    
    var
      Module: TXDataServerModule;
      Server: THttpSysServer;
    begin
      Server := THttpSysServer.Create;
      Module := CreateXDataServerModule('http://server:2001/tms/xdata');
      Server.AddModule(Module);
      Server.Start;
      ReadLn;
      Server.Stop;
      Server.Free;
    end;
    

    The code above will create and start an XData server that will receive and respond to HTTP requests at the address "http://server:2001/tms/xdata".

    TXDataServerModule

    To create an XData server, you need to add a TXDataServerModule object to the Sparkle HTTP Server. As with any Sparkle module, it will have a base (root) URL associated with it, and it will respond to requests sent to any URL which matches the root URL of the module.

    The TXDataServerModule is the main XData class, because it is the one which receives and handles all HTTP requests. So in a way, that is the class that implements the XData server. Although it is a very important class, its usage is very simple. You need to create the class using one of the overloaded constructors (TXDataServerModule is declared in unit XData.Server.Module):

    constructor Create(const ABaseUrl: string); overload;
    constructor Create(const ABaseUrl: string; AConnectionPool: IDBConnectionPool); overload;
    constructor Create(const ABaseUrl: string; AConnectionPool: IDBConnectionPool;
      AModel: TXDataAureliusModel); overload;
    constructor Create(const ABaseUrl: string; AConnection: IDBConnection); overload;
    constructor Create(const ABaseUrl: string; AConnection: IDBConnection;
      AModel: TXDataAureliusModel); overload;
    

    In summary, you must provide the base URL and optionally an IDBConnectionPool interface so that the server can retrieve IDBConnection interfaces to operate with the database (if database connectivity is desired). For example:

    XDataServerModule := TXDataServerModule.Create('http://server:2001/tms/xdata',
      TDBConnectionPool.Create(50,
        TDBConnectionFactory.Create(
          function: IDBConnection
          var
            MyDataModule: TMyDataModule;
          begin
            MyDataModule := TMyDataModule.Create(nil);
            Result := TFireDacConnectionAdapter.Create(MyDataModule.FDConnection1, MyDataModule);
          end
    )));
    

    The example above creates the server with the root URL "http://server:2001/tms/xdata" providing a connection pool of a maximum of 50 simultaneous connections. The database connection used will be a FireDac TFDConnection component named FDConnection1, declared in a data module named TMyDataModule. That is all you need to set up for your XData server to run and to expose your Aurelius objects.

    The first overloaded constructor, used in the previous example, takes just two parameters: the base url and the connection pool. The other overloaded versions are just variations that provide different settings.

    If you have multiple Aurelius mapping models in your application, you can optionally use an entity model different from default, or explicitly build an entity model and provide it in the constructor for specific mapping. This allows for better control which classes and mapping settings are available from the XData server. If the model is not provided, XData uses the default entity model which in turn uses the default Aurelius model. Note that the model passed to the constructor will not be owned by the server module and must be destroyed manually. For example, the following code creates a module using the "Sample" model:

    XDataServerModule := TXDataServerModule.Create('http://server:2001/tms/xdata',
      MyConnectionPool, TXDataAureliusModel.Get('Sample'));
    

    There are also versions of the Create constructor that receive an IDBConnection interface instead of an IDBConnectionPool. Those are easy-to-use variations that internally create a connection pool with a single connection (no simultaneous connections available). It is an easy approach for testing and debug purposes, but should not be used in production environments because performance might not be ideal.

    Properties

    Name Description
    UserName: string
    Password: string
    TXDataServerModule provides these properties to specify UserName and Password required by the server using Basic Authentication. By default, these values are empty which means no authentication is performed and any client can access server resources and operations. When basic authentication is used, be sure to use HTTP secure (HTTPS) if you do not want your user name or password to be retrieved by middle-man attacks. If you do not use it, both user name and password are transmitted in plain text in HTTP requests.

    These properties provide a very limited basic authentication mechanism. For a more advanced variant, you should use the TMS Sparkle built-in Basic Authentication mechanism.
    AccessControlAllowOrigin: string Specifies the accepted client hosts for which CORS will be enabled. If you want to accept any client connection, set this property value to '*'. This will enable CORS in the server including proper responses to preflighted requests.
    DefaultExpandLevel: integer Defines the minimum level that associated entities will be expanded (included inline) in JSON responses. Default value is 0 meaning that all associated entities will be represented as references unless specified otherwise. Clients can override this value by using xdata-expand-level header.
    Events: TXDataModuleEvents Container for server-side events.
    PutMode: TXDataPutMode Defines how PUT will be implemented at server side with Aurelius: Either TXDataPutMode.Update or TXDataPutMode.Merge method (default). You will rarely need to change this property unless to ensure backward compatibility with older versions. This property value can be overridden in a specific request by using xdata-put-mode HTTP Request header.
    SerializeInstanceRef: TInstanceRefSerialization Controls how instance reference ($ref) will appear in JSON response. See below for options.

    This property value can be overridden in a specific request by using xdata-serialize-instance-ref HTTP Request header.
    SerializeInstanceType: TInstanceTypeSerialization Controls whenever the entity/object type appears in the JSON response (property annotation "xdata.type"). See below for options.

    This property value can be overridden in a specific request by using xdata-serialize-instance-type HTTP Request header.
    UnknownMemberHandling: TUnknownMemberHandling Defines server behavior when receiving JSON from the client with a property that is not known for that request. For example, JSON representing an entity with a property that does not belong to that entity. See below for options.
    RoutingPrecedence: TRoutingPrecedence Specifies which route to use when there is a conflict between URL endpoints defined by automatic CRUD endpoints and service operations . See below for options.
    EnableEntityKeyAsSegment: Boolean When True, it's possible to address single entities by using the URL format "/entityset/id" - in addition to the default "/entityset(id)". Default is False.
    SwaggerOptions: TSwaggerOptions
    SwaggerUIOptions: TSwaggerUIOptions
    Provide access to configure Swagger and SwaggerUI behavior. See more information at OpenAPI/Swagger Support.

    TInstanceRefSerialization

    • TInstanceRefSerialization.Always: $ref is always used if the same instance appears again in the JSON tree. This mode is more optimized for use with TXDataClient, and is the default option.

    • TInstanceRefSerialization.IfRecursive: $ref is only used if the instance appears as an associated object of itself. If the instance appears in a non-recursive way, $ref is not used and the instance is fully serialized inline instead. This mode is more suited for JavaScript and other non-Delphi clients so those clients do not need to resolve the $ref objects.

    TInstanceTypeSerialization

    • TInstanceTypeSerialization.Always: The xdata.type annotation always appears in JSON responses.

    • TInstanceTypeSerialization.IfNeeded: The xdata.type annotation is only present if the entity/object type is a descendant of the expected type. For example, suppose a GET request is performed in url Customers. For any entity in JSON that is of type Customer, the annotation will not present. If the entity in JSON is a descendant of Customer (e.g., DerivedCustomer) then xdata.type apppears. Basically, it means that xdata.type is implicit when absent and is of the expected type of the request/specification.

    TUnknownMemberHandling

    • TUnknownMemberHandling.Error: An InvalidJsonProperty error is raised if JSON contains an invalid property. This is the default behavior.

    • TUnknownMemberHandling.Ignore: Invalid properties in JSON will be ignored and the request will be processed.

    TRoutingPrecedence

    • TRoutingPrecedence.Crud: The URL from automatic CRUD endpoint will be used if there is a URL conflict with service operation endpoints. This is the default behavior.

    • TRoutingPrecedence.Service: The URL from service operation will be used if there is a URL conflict with automatic CRUD endpoints.

    Methods

    Name Description
    procedure SetEntitySetPermissions(const EntitySetName: string; Permissions: TEntitySetPermissions) Specify the permissions for a specified entity set.

    IDBConnectionPool Interface

    The IDBConnectionPool interface is used by the XData server module to retrieve an IDBConnection interface used to connect to a database. As client requests arrive, the XData server needs an IDBConnection interface to connect to the database server and execute the SQL statements. It achieves this by trying to acquire an IDBConnection interface from the IDBConnectionPool interface. Thus, providing an IDBConnectionPool interface to the XData server module is mandatory for this to work.

    The IDBConnectionPool interface is declared in the unit Aurelius.Drivers.Interfaces and contains a single GetConnection method:

    IDBConnectionPool = interface
      function GetConnection: IDBConnection;
    end;
    

    The easiest way to create an IDBConnectionPool interface is by using the TDBConnectionPool class which implements the IDBConnectionPool interface. To instantiate it, you need to call the Create constructor passing an IDBConnectionFactory interface which will be used by the connection pool to create a new connection if needed. Alternatively, you can pass an anonymous method that creates and returns a new IDBConnection interface each time it is called.

    Also, you need to specify the maximum number of IDBConnection interfaces (database connections) available in the pool. Whenever a client request arrives, the XData server acquires an IDBConnection from the pool, does all the database processing with it, and then returns it to the pool. If many simultaneous requests arrive, the pool might run out of available connections when the number of acquired IDBConnection interfaces reaches the maximum number specified. If that happens, the client request will wait until a new connection is available in the pool. TDBConnectionPool class is declared in the unit XData.Aurelius.ConnectionPool.

    The following code illustrates how to create an IDBConnectionPool interface. It uses a function named CreateMyConnectionFactory which is not shown in this example. To learn how to create such interface, please refer to IDBConnectionFactory topic.

    uses 
      {...}, Aurelius.Drivers.Interfaces, XData.Aurelius.ConnectionPool;
    
    var
      ConnectionPool: IDBConnectionPool;
      ConnectionFactory: IDBConnectionFactory;
    begin
      ConnectionFactory := CreateMyConnectionFactory; //  Creates IDBConnectionFactory
      ConnectionPool := TDBConnectionPool.Create(
        50, // maximum of 50 connections available in the pool
            // Define a number that best fits your needs
        ConnectionFactory
      );
    
      // Use the IDBConnectionPool interface to create the XData server module
    end;
    

    Alternatively, you can just provide an anonymous method that creates the IDBConnection instead of providing the IDBConnectionFactory interface:

    uses 
      {...}, Aurelius.Drivers.Interfaces, XData.Aurelius.ConnectionPool;
    
    var
      ConnectionPool: IDBConnectionPool;
    begin
      ConnectionPool := TDBConnectionPool.Create(
        50, // maximum of 50 connections available in the pool
        function: IDBConnection
        var
          SQLConn: TSQLConnection;
        begin
          // Create the IDBConnection interface here
          // Be sure to also create a new instance of the database-access component here
          // Two different IDBConnection interfaces should not share
          // the same database-access component
    
          // Example using dbExpress
          SQLConn := TSQLConnection.Create(nil);
          { Define SQLConn connection settings here, the server
             to be connected, user name, password, database, etc. }
          Result := TDBExpressConnectionAdapter.Create(SQLConn, true);
        end
      ));
    
      // Use the IDBConnectionPool interface to create the XData server module
    end;
    

    If you do not need a pooling mechanism but just want one database connection to be created for each time someone asks for a connection from the pool, you can use the TDBConnectionFactory class. It also implements the IDBConnectionPool interface:

    var
      ConnectionPool: IDBConnectionPool;
    begin
      ConnectionPool := TDBConnectionFactory.Create(
          function: IDBConnection
          var
            MyDataModule: TMyDataModule;
          begin
            // Creates a datamodule which contains a 
            // TSQLConnection component that is already configured
            MyDataModule := TMyDataModule.Create(nil);
    
            // The second parameter makes sure the data module will be destroyed
            // when IDBConnection interface is released
            Result := TDBExpressConnectionAdapter.Create(
              MyDataModule.SQLConnection1, MyDataModule);
          end
        ));
    end;
    

    IDBConnectionFactory Interface

    The IDBConnectionFactory interface is used to create an IDBConnectionPool interface used by the XData module. As client requests arrive, the XData server needs to retrieve an IDBConnection interface from the IDBConnectionPool so it can perform operations on the database. The connection pool creates a new IDBConnection by calling IDBConnectionFactory.CreateConnection method.

    The IDBConnectionFactory interface is declared in unit Aurelius.Drivers.Interfaces, and it contains a single CreateConnection method:

    IDBConnectionFactory = interface
      function CreateConnection: IDBConnection;
    end;
    

    The easiest way to create such an interface is using the TDBConnectionFactory class which implements the IDBConnectionFactory interface. To create a TDBConnectionFactory object, you just need to pass an anonymous method that creates and returns a new IDBConnection interface each time it is called. The TDBConnectionFactory class is declared in the unit Aurelius.Drivers.Base.

    The IDBConnection interface is part of the TMS Aurelius library used by XData. You can refer to the TMS Aurelius documentation to learn how to create the IDBConnection interface.

    In the following example, the factory will create an IDBConnection pointing to a Microsoft SQL Server database using a dbExpress connection. You can connect to many other database servers (Oracle, Firebird, MySQL, etc.) using many different database-access components (FireDac, dbExpress, UniDac, ADO, etc.). Please refer to Database Connectivity topic on TMS Aurelius documentation to learn about all those options. Regardless of what you use, the structure of the following code will be the same. What will change only is the content of the function: IDBConnection.

    uses 
      {...}, Aurelius.Drivers.Interfaces, Aurelius.Drivers.Base,
      Aurelius.Drivers.dbExpress;
    
    var
      ConnectionFactory: IDBConnectionFactory;
    begin
      ConnectionFactory := TDBConnectionFactory.Create(
          function: IDBConnection
          var
            conn: TSQLConnection;
          begin
            // Create the IDBConnection interface here
            // Be sure to also create a new instance of the database-access component here
            // Two different IDBConnection interfaces should not share
            // the same database-access component
    
            // Example using dbExpress
            conn := TSQLConnection.Create(nil);
            conn.DriverName := 'MSSQL';
            conn.GetDriverFunc := 'getSQLDriverMSSQL';
            conn.VendorLib := 'sqlncli10.dll';
            conn.LibraryName := 'dbxmss.dll';
            conn.Params.Values['HostName'] := 'server';
            conn.Params.Values['Database'] := 'master';
            conn.Params.Values['User_Name'] := 'sa';
            conn.Params.Values['Password'] := 'password';
            conn.LoginPrompt := false;
            Result := TDBExpressConnectionAdapter.Create(SQLConn, true);
          end
        ));
    
      // Use the ConnectionFactory interface to create an IDBConnectionPool interface
    end;
    

    It is possible that you already have your database-access component configured in a TDataModule and you do not want to create it in code. In this case, you can just create a new instance of the data module and return the associated IDBConnection to the component. But you must be sure to destroy the data module as well (not only the database-access component) to avoid memory leaks:

    var
      ConnectionFactory: IDBConnectionFactory;
    begin
      ConnectionFactory := TDBConnectionFactory.Create(
          function: IDBConnection
          var
            MyDataModule: TMyDataModule;
          begin
            // Creates a datamodule which contains a 
            // TSQLConnection component that is already configured
            MyDataModule := TMyDataModule.Create(nil);
    
            // The second parameter makes sure the data module will be destroyed
            // when IDBConnection interface is released
            Result := TDBExpressConnectionAdapter.Create(
              MyDataModule.SQLConnection1, MyDataModule);
          end
        ));
    
      // Use the ConnectionFactory interface to create an IDBConnectionPool interface
    end;
    

    OpenAPI/Swagger Support

    Note

    Documentation about OpenAPI, SwaggerUI and Redoc has been moved to its own chapter: OpenAPI Support.

    In This Article
    Back to top TMS XData v5.21
    © 2002 - 2025 tmssoftware.com