Getting Started

June 1, 2026 · View on GitHub

Read this in English or Português (BR).

This page takes you from a blank project to a running Horse server in under five minutes. It assumes you have Delphi 10.4 or later (or Lazarus 2.2 / FPC 3.2+) and a working internet connection for Boss to fetch dependencies.

If anything below doesn't apply to your environment, check Compiler Support first.


1. Install Boss

Boss is the package manager Horse uses for distribution. One-time install:

# Windows (PowerShell, run as administrator)
iwr https://github.com/HashLoad/boss/releases/latest/download/boss-windows.exe -OutFile boss.exe
Move-Item boss.exe C:\Windows\System32\

# Linux / macOS
curl -L https://github.com/HashLoad/boss/releases/latest/download/boss-linux -o boss
chmod +x boss
sudo mv boss /usr/local/bin/

Verify:

boss --version

2. Create a project and add Horse

Create a fresh Delphi console application (or Lazarus project), save it, then from a terminal in the project directory:

boss init -q                  # writes a boss.json
boss install horse            # adds Horse to the project search path

Boss fetches the latest stable Horse, drops it into modules/horse/, and adds the source path to your .dproj / .lpi automatically. You can now uses Horse; from any source file.

3. Minimal server — Delphi

program HelloHorse;

{$APPTYPE CONSOLE}

uses
  Horse;

begin
  THorse.Get('/ping',
    procedure(Req: THorseRequest; Res: THorseResponse)
    begin
      Res.Send('pong');
    end);

  WriteLn('Listening on http://127.0.0.1:9000/ping');
  THorse.Listen(9000);
end.

Compile, run, then in another terminal:

curl http://127.0.0.1:9000/ping
# pong

That's it. Press Ctrl-C to stop.

4. Minimal server — Lazarus / FPC

program HelloHorse;

{$MODE DELPHI}{$H+}

uses
  Horse;

procedure GetPing(Req: THorseRequest; Res: THorseResponse);
begin
  Res.Send('pong');
end;

begin
  THorse.Get('/ping', @GetPing);
  THorse.Listen(9000);
end.

Two FPC-specific notes:

  • {$MODE DELPHI}{$H+} is required so anonymous procedures and string = AnsiString semantics match Delphi.
  • Route callbacks take an @ because Lazarus distinguishes procedure values from procedure references.

5. Project structure conventions

You don't have to follow these — Horse imposes no layout — but the official samples use them:

my-server/
├── boss.json                ← Boss manifest
├── boss-lock.json           ← Pinned versions (auto-generated)
├── modules/                 ← Boss-installed dependencies (gitignored)
├── src/
│   ├── HelloHorse.dpr       ← Entry point (registers routes, calls Listen)
│   ├── Controllers/         ← One unit per resource (Users, Products, ...)
│   ├── Services/            ← Business logic, no Horse dependency
│   └── Middlewares/         ← Cross-cutting concerns
└── tests/
    └── ...

A typical entry point composes the application:

uses
  Horse, Horse.Jhonson, Horse.CORS,
  Controllers.Users, Controllers.Products;

begin
  THorse
    .Use(Jhonson)                 // JSON body parsing
    .Use(CORS);                    // permissive CORS

  Controllers.Users.RegisterRoutes;
  Controllers.Products.RegisterRoutes;

  THorse.Listen(9000);
end.

…and each controller exposes a RegisterRoutes procedure that calls THorse.Get/Post/Put/….

6. Same code, seven deployment shapes

The Console hello-world above is the simplest deployment shape, but the same THorse.Get / THorse.Listen code can ship as any of these, by changing only the project type and Conditional Defines:

You want to ship as…CompileAdd a HORSE_* define?One-line shutdown hook
Console binaryDelphi or FPC(none) — what you just builtSetConsoleCtrlHandler (Win) / signal SIGTERM (Linux)
VCL desktop app embedding a serverDelphi(none — write a Forms project)FormClose
Linux daemonDelphi or FPC, target Linux64(none — console binary + systemd)signal SIGTERM / fpSignal SIGTERM
Windows ServiceDelphi (Service Application)(none — write a TService project)TService.OnStop
Lazarus LCL desktop appFPC(none — write a Lazarus project)FormClose
Apache moduleDelphiHORSE_APACHE(Apache owns the lifecycle)
IIS ISAPI extensionDelphiHORSE_ISAPI(IIS owns the lifecycle)
CGI / FastCGI binaryDelphi or FPCHORSE_CGI / HORSE_FCGI(host owns the lifecycle)

For the high-performance path, add one of the following Provider defines alongside (where the table says "none") to switch the transport from the default Indy / fphttpserver to an async Provider:

Provider defineBacked byWhen to pick it
HORSE_PROVIDER_CROSSSOCKETwinddriver/Delphi-Cross-Socket (upstream) + cnpack/cnvcl for CnPack/Crypto units — IOCP / epoll / kqueue. Install both manually (mirroring the mORMot2 setup). For mTLS server mode (SSLVerifyPeer = True), use the supported alternative freitasjca/Delphi-Cross-Socket v1.0.3 which bundles CnPack and adds SetCACertificateFile + SetVerifyPeer in one clone.You prefer native async control or already depend on Delphi-Cross-Socket. Requires Delphi 10.2+.
HORSE_PROVIDER_MORMOTmORMot2 THttpServer — IOCP / epollYou want Delphi 7+ compatibility, http.sys kernel-mode HTTP, or pure-Pascal HTTP without compiled C deps.

The two Providers are mutually exclusive (one transport per build). The legacy alias HORSE_CROSSSOCKET keeps working forever (PATCH-HORSE-2 translates it to HORSE_PROVIDER_CROSSSOCKET); there is no legacy alias for mORMot — it's new.

Concrete recipes (project type, code skeleton, install commands) for each shape: Deployment Cheatsheet, or the longer-form Providers & Application types §8 (CrossSocket) / §9 (mORMot2).

7. Where to next

Troubleshooting

SymptomLikely cause
Unit not found Horseboss install horse didn't run — re-run it from the project directory.
Server starts then exits immediatelyThe Listen call is non-blocking on some platforms; in a console app make sure the main thread doesn't fall through to end. Wrap it with a ReadLn or use a signal handler.
Port already in useAnother service holds :9000. Pick another port or stop the conflict.
Address already in use after a previous run crashedThe previous process didn't release the socket. Wait 30 s for TIME_WAIT, or change the port.
Compilation fails on FPC with "method-pointer expected"Missing @ before the procedure name in THorse.Get(...).