sgcOpenAPI 2026.6 — Standalone OpenAPI Server, Spec-First or Code-First

· Releases

The next release of sgcOpenAPI — version 2026.6, due in June — ships a brand-new component: TsgcHTTPServer_OpenAPI. It is a single, self-contained Delphi component that hosts an OpenAPI 3.0 service with an embedded HTTP server. Drop it on a form, point it at a spec (or generate one from a Delphi class with RTTI attributes), set Active := True, and you have a documented REST API with auto-served Swagger UI.

The headline change is that sgcOpenAPI no longer needs sgcWebSockets to host an HTTP server. The new component is shipped, packaged and installed entirely from sgcOpenAPI. If you already use sgcWebSockets the previous TsgcWSServer_API_OpenAPI component keeps working unchanged — both share the same engine internally.

What you get in one component

TsgcHTTPServer_OpenAPI bundles three things:

Quick start — the minimum example

This is everything you need to host a working OpenAPI server with Swagger UI:

uses
  sgcHTTPServer_OpenAPI;

var
  oServer: TsgcHTTPServer_OpenAPI;
begin
  oServer := TsgcHTTPServer_OpenAPI.Create(nil);
  try
    oServer.Bindings.Add.Port := 8080;
    oServer.LoadFromFile('petstore.json');
    oServer.OnRequest := MyOnRequest;
    oServer.Active := True;
    Readln;
  finally
    oServer.Free;
  end;
end;

Browse to http://localhost:8080/docs for the Swagger UI and http://localhost:8080/openapi.json for the spec. Every operation defined in the spec is routed to your MyOnRequest handler with the resolved operationId and a fully-built request context.

Spec-First — load an existing OpenAPI 3.0 file

If you already have an OpenAPI 3.0 JSON or YAML file (Petstore, an internal API contract, a public schema you want to mock), spec-first is the fastest way to serve it. LoadFromFile reads and parses the spec, builds a route table from the paths section, and matches every incoming request against it.

Each route's operationId is the dispatch key. Inside OnRequest you handle each operation in turn:

uses
  sgcHTTP_OpenAPI_Server, sgcHTTP_OpenAPI_Server_Engine,
  sgcHTTPServer_OpenAPI;

procedure TForm1.OnOpenAPIRequest(Sender: TObject;
  const aOperationId: string; const aContext: TsgcOpenAPIServerContext;
  var Handled: Boolean);
begin
  Handled := True;
  if aOperationId = 'listPets' then
    HandleListPets(aContext)
  else if aOperationId = 'getPetById' then
    HandleGetPetById(aContext)
  else if aOperationId = 'createPet' then
    HandleCreatePet(aContext)
  else
    Handled := False;
end;

procedure TForm1.HandleGetPetById(const aContext: TsgcOpenAPIServerContext);
var
  vId, vPetJSON: string;
begin
  vId := aContext.PathParamAsString('petId');
  vPetJSON := FPets.Values[vId];
  if vPetJSON <> '' then
    aContext.RespondJSON(200, vPetJSON)
  else
    aContext.RespondError(404, 'Not Found', 'Pet ' + vId + ' not found');
end;

The TsgcOpenAPIServerContext gives you typed accessors for everything in the request: PathParamAsString / PathParamAsInteger for templated segments, QueryParamAsString / QueryParamAsInteger / QueryParamAsBoolean with default values, BodyAsString / BodyAsJSON for the request body, and HeaderValue for any incoming header. To respond, use the helpers RespondJSON(code, content) and RespondError(code, title, detail), or set Response.Code, Response.ContentType and Response.Content directly for full control.

Code-First — generate the spec from a Delphi class

If you would rather write the API contract in Delphi and let the spec be generated, decorate a class with RTTI attributes. TsgcOpenAPICodeFirstScanner walks the class, builds a complete OpenAPI 3.0 JSON document, and you load that into the server with LoadFromString. This requires Delphi XE7 or newer (for extended RTTI).

uses
  sgcHTTP_OpenAPI_Server_CodeFirst;

type
  [sgcServiceContract('Task Manager API',
    'A simple task management demo', '1.0.0')]
  [sgcRoute('/api/v1')]
  TTaskManagerService = class
  public
    [sgcHttpGet]
    [sgcRoute('/tasks')]
    [sgcSummary('List all tasks')]
    [sgcTag('Tasks')]
    [sgcResponse(200, 'A list of tasks')]
    procedure ListTasks([sgcFromQuery] const status: string); virtual;

    [sgcHttpPost]
    [sgcRoute('/tasks')]
    [sgcSummary('Create a new task')]
    [sgcTag('Tasks')]
    [sgcResponse(201, 'Task created successfully')]
    procedure CreateTask([sgcFromBody] const body: string); virtual;

    [sgcHttpGet]
    [sgcRoute('/tasks/{taskId}')]
    [sgcSummary('Get a task by ID')]
    [sgcTag('Tasks')]
    [sgcResponse(200, 'The requested task')]
    [sgcResponse(404, 'Task not found')]
    procedure GetTask([sgcFromPath][sgcRequired]
      const taskId: Integer); virtual;
  end;

The method bodies are stubs — they exist only so the compiler emits RTTI for them. The real work happens in OnRequest, dispatched by the operationId that the scanner derives from each method name (ListTasks, CreateTask, GetTask…).

Hand the class to the scanner at startup and load the generated spec into the server:

uses
  sgcHTTP_OpenAPI_Server_CodeFirst, sgcHTTPServer_OpenAPI;

var
  oScanner: TsgcOpenAPICodeFirstScanner;
  oServer: TsgcHTTPServer_OpenAPI;
  vSpec: string;
begin
  oScanner := TsgcOpenAPICodeFirstScanner.Create;
  try
    vSpec := oScanner.GenerateSpec(TTaskManagerService);
  finally
    oScanner.Free;
  end;

  oServer := TsgcHTTPServer_OpenAPI.Create(nil);
  oServer.LoadFromString(vSpec);
  oServer.Bindings.Add.Port := 8081;
  oServer.OnRequest := MyOnRequest;
  oServer.Active := True;
end;

The attributes cover the common metadata: sgcServiceContract populates the OpenAPI info block, sgcRoute sets the path at class or method level, sgcHttpGet / Post / Put / Delete / Patch / Head / Options picks the verb, sgcSummary and sgcDescription document the operation, sgcTag groups it in Swagger UI, sgcResponse(code, description) declares each response, and sgcFromPath / FromQuery / FromBody / FromHeader together with sgcRequired describe each parameter.

Configuration — OpenAPIOptions

All server-side configuration sits under OpenAPIOptions, grouped into three sub-options:

oServer.OpenAPIOptions.Endpoint.BasePath        := '/api';
oServer.OpenAPIOptions.Endpoint.ServeSpec       := True;   // /openapi.json
oServer.OpenAPIOptions.Endpoint.ServeSwaggerUI  := True;   // /docs

oServer.OpenAPIOptions.CORS.Enabled             := True;
oServer.OpenAPIOptions.CORS.AllowOrigins        := '*';
oServer.OpenAPIOptions.CORS.AllowHeaders        := 'Content-Type, Authorization';
oServer.OpenAPIOptions.CORS.AllowMethods        := 'GET, POST, PUT, DELETE, PATCH, OPTIONS';

oServer.OpenAPIOptions.Validation.ValidateRequest     := True;
oServer.OpenAPIOptions.Validation.ValidateRequestBody := True;
oServer.OpenAPIOptions.Validation.ValidateQueryParams := True;
oServer.OpenAPIOptions.Validation.ValidatePathParams  := True;
oServer.OpenAPIOptions.Validation.ValidateRequired    := True;

With validation on, every incoming request is checked against the JSON Schemas declared in the spec before it reaches your handler — required fields, types, formats, enums, ranges. Failures fire the OnValidationError event with the list of errors and a flag to accept or reject the request.

Events

The component exposes six events for the request lifecycle:

OnBeforeRequest: fires before dispatch; set Accept := False to reject with a 403 Forbidden. Useful for rate-limiting, logging, or per-route gates.

OnAuthenticate: fires before the main handler; set Authenticated := False to reject with 401 Unauthorized. Inspect headers, cookies or query parameters to decide.

OnValidationError: fires when validation fails; receives the list of errors. Set Continue := False to reject with 400 Bad Request.

OnRequest: the main dispatch event. Look at aOperationId, write the response into aContext.Response, set Handled := True.

OnAfterRequest: fires after the handler returns — ideal for metrics or audit logging.

OnException: fires if an unhandled exception bubbles out of your handler. Adjust aResponseCode if you want something other than 500 Internal Server Error.

Demos

Two complete demos ship with sgcOpenAPI 2026.6, both using only the new standalone component — no sgcWebSockets installation required:

Upgrading

If you currently use TsgcWSServer_API_OpenAPI with sgcWebSockets, nothing changes — the class, properties and events are all preserved and the implementation now delegates to the same shared engine that powers the new standalone component. You can keep your existing code as-is, or migrate one form at a time to TsgcHTTPServer_OpenAPI to drop the sgcWebSockets dependency.

sgcOpenAPI 2026.6 will be available on the downloads page in June.

Questions, feedback or migration help? Get in touch — you will get a reply from the people who wrote the code.