Skip to content

Getting started with WiRL Server

In this tutorial we will see the simplest way to develop a WiRL server. If during installation you installed the designtime package WiRLDesign.dproj in Delphi, under File|New|Other, you will have a new option "WiRL Server Application Wizard" which creates a skeleton for a WiRL application.

WiRL Server Application Wizard

But now we will see a step by step guide on how to configure your server manually in order to have a better understanding of WiRL internal.

Console application

Let's start by creating a console application, it can be good choice for development and eventually can be easily transformed into a Windows service for the production environment.

Let's reduce the code to the bare bones and transform the one generated by Delphi into this:

pascal
program Project1;

{$APPTYPE CONSOLE}

uses
  System.SysUtils;

begin
  Readln;
end.

Now we have an application that opens a console and ends when the enter key is pressed.

Creating the server

First we will have to create a TWiRLServer object which will listen on a specific port and which will route the HTTP traffic. If you try to activate the server (via the property Active) you will receive an error at runtime:

Project Project1.exe raised exception class EWiRLException with message 'CreateServer: no server registered (add "WiRL.http.Server.*" unit to the project)'.

This is because the structure of TWiRLServer allows it to open the HTTP channel via different libraries; and we have not yet indicated which library to use. In this case we will use the Indy components (already installed in Delphi). To do this, simply add the unit WiRL.http.Server.Indy.

pascal
program Project1;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,

  WiRL.http.Server.Indy,
  WiRL.http.Server;

var
  LServer: TWiRLServer;

begin
  LServer := TWiRLServer.Create(nil);
  try
    LServer.SetPort(8080);
    LServer.Active := True;

    Writeln('Server running at http://localhost:' + LServer.Port.ToString + '/');
    Readln;
  finally
    LServer.Free;
  end;
end.

Configure the engine

With the code we have already written we have a working HTTP server but any connection attempt will return the error:

Project Project1.exe raised exception class EWiRLNotFoundException with message 'Engine not found for URL [/test]'.

This is because we haven't configured the Engine yet. The engine is the WiRL module that is responsible for interpreting the call. There are currently two engines: TWiRLEngine and TWiRLFileSystemEngine. The first is the main WIRL engine that handles ReST calls and is what we will see in this tutorial, while the second allows you to return files (html, js, css, etc.). It is also possible to implement custom Engines, for example to manage SOAP and GraphQL calls .

Each engine is associated with a path, in this way WiRL is able to understand the engine to use through the requested URL with a code similar to this:

pascal
uses
  ...
  WiRL.Core.Engine,
  ...

begin
  ...
  // http://localhost:8080/rest/...
  LServer.AddEngine<TWiRLEngine>('rest');
  ...

Configure the application

Then it's possible to configure the different WiRL modules, for example:

  • Resources
  • Filters
  • Message body writer e read
  • External libraries (e.g. neon and JWT)

WiRL has a virtual application concept. In this way it is possible to create multiple modules with different configurations in a single physical application. In this case, for the sake of simplicity, we will only use one virtual application with the minimum configuration.

pascal
program Project1;

{$APPTYPE CONSOLE}

uses
  System.SysUtils,

  WiRL.http.Server.Indy,
  WiRL.http.Server,

  WiRL.Core.Engine;

var
  LServer: TWiRLServer;

begin
  LServer := TWiRLServer.Create(nil);
  try
    LServer.SetPort(8080);
    LServer.Active := True;

    // http://localhost:8080/rest/...
    LServer.AddEngine<TWiRLEngine>('rest')
      // http://localhost:8080/rest/app/...
      .AddApplication('app')
      .SetResources('*');

    Writeln('Server running at http://localhost:' + LServer.Port.ToString + '/');
    Readln;
  finally
    LServer.Free;
  end;
end.

In this way we are going to create the application named app, reachable via the path of the same name, containing all the resources.

With this we have finished the configuration but the resources are still missing.

The first resource

WiRL is able to handle (both input and output) different types of data, from the simplest ones, such as strings or integers, to complex objects including streams, collections, DataSets and so on. These will be our ReST resources. Numerous examples can be found in the demos present in the sources. Here we will see how to return a simple object like TPerson as JSON.

Let's start by defining the class TPerson:

pascal
  TPerson = class(TObject)
  private
    FName: string;
  public
    property Name: string read FName write FName;
  end;

Now we need to create a class that can create the TPerson object. In a more complex application this class would probably read the object from a database or something similar. The class should also be responsible for modifying the object. But for this example we will only create the object initialized by the method arguments:

pascal
  TPersonResource = class
  public
    function GetPerson(const AName: string): TPerson;
  end;

function TPersonResource.GetPerson(const AName: string): TPerson;
begin
  Result := TPerson.Create;
  Result.Name := AName;
end;

What we see is a very basic class with a method GetPerson that creates our person. All we have to do is explain to WiRL how to use the class TPersonResource to respond to HTTP calls. WiRL allows us to do this by decorating the class and methods with attributes:

pascal
  [Path('person')]
  TPersonResource = class
  public
    [GET]
    [Produces(TMediaType.APPLICATION_JSON)]
    function GetPerson([QueryParam('name')] const AName: string): TPerson;
  end;

The attributes (declared in the unit WiRL.Core.Attributes) are:

  • Path: (Applied to a class o method) The path of the resource. When a ReST call arrives with that path WiRL will instantiate the class and destroy it at the end of the call. It can also be applied to a method and in that case it indicates a "sub-resource". The method path and the class path will be used together to get the URL of the sub-resource.
  • GET: (Applied to a method of the class) That instance method will be called when it matches with the HTTP method (others HTTP method attributes are PUT, POST, DELETE and so on).
  • Produce: (Applied to a method) WiRL call the method ONLY if the client requests the right media type (Accept request header). There is also the attribute Consumes that is needed when data also arrives in the body of the HTTP request. In that case Consumes it indicates if our method is able to accept that type of content.
  • QueryParam: It is used on a request parameter. In this case it indicates that the parameter must be read from the query string indicated in the URL. There are also PathParam, FormParam, BodyParam and many others.

Now we need to register our resource:

pascal
TWiRLResourceRegistry.Instance.RegisterResource<TPersonResource>;

And by starting the program we can point the browser to:

http://localhost:8080/rest/app/person?name=luca

We will receive the JSON corresponding to the class TPerson. To understand the correct URL to use we can follow this scheme:

Url

Here a summary of the classes involved in the URL parsing:

  • TWiRLServer: set the port with the method SetPort( 8080 )
  • TWiRLEngine: for the first part of the URL with AddEngine( rest )
  • TWiRLApplcation: via method AddApplication( app )
  • The resource: via the Path (person) attribute

Conclusions

With this we have our first example up and running.

You can find the complete code on GitHub and many more examples in the Demo folder of the WiRL repository.