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

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

The next release of sgcOpenAPI, version 2026.6, due in June, ships an OpenAPI 3.0 server you can host without installing sgcWebSockets. Two components do it: TsgcHTTPServer, the Indy based HTTP server, and TsgcWSAPIServer_OpenAPI, the API component you attach to it. Point the API component at a spec (or generate one from a Delphi class with RTTI attributes), assign its Server property, start the HTTP server, 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. Both components are shipped, packaged and installed entirely from sgcOpenAPI. If you already use sgcWebSockets, the TsgcWSServer_API_OpenAPI component you know keeps working unchanged, because it is the same class: both products build it from the same engine.

What you get

The pair brings three things:

Quick start — the minimum example

This is everything you need to host a working OpenAPI server with Swagger UI. Note that the API component has no Active property of its own: assigning Server is what attaches it, and assigning nil detaches it while the HTTP server keeps running.

uses
  sgcHTTP_Server, sgcWebSocket_Server_API_OpenAPI;

var
  oServer: TsgcHTTPServer;
  oOpenAPI: TsgcWSAPIServer_OpenAPI;
begin
  oServer := TsgcHTTPServer.Create(nil);
  oOpenAPI := TsgcWSAPIServer_OpenAPI.Create(nil);
  try
    oServer.Bindings.Add.Port := 8080;
    oOpenAPI.LoadFromFile('petstore.json');
    oOpenAPI.OnRequest := MyOnRequest;
    oOpenAPI.Server := oServer;
    oServer.Active := True;
    Readln;
  finally
    oOpenAPI.Free;
    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 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. The server reads JSON, so convert a YAML contract first, for example with sgcOpenAPI.exe.

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

uses
  sgcHTTP_Server, sgcHTTP_OpenAPI_Server,
  sgcHTTP_OpenAPI_Server_Engine, sgcWebSocket_Server_API_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_Server, sgcHTTP_OpenAPI_Server_CodeFirst,
  sgcWebSocket_Server_API_OpenAPI;

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

  oServer := TsgcHTTPServer.Create(nil);
  oServer.Bindings.Add.Port := 8081;

  oOpenAPI := TsgcWSAPIServer_OpenAPI.Create(nil);
  oOpenAPI.LoadFromString(vSpec);
  oOpenAPI.OnRequest := MyOnRequest;
  oOpenAPI.Server := oServer;

  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 on the API component, grouped into five sub-options. These three carry the everyday settings:

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

Six events cover 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.

The two remaining sub-options add their own: Security drives OnValidateAPIKey, OnValidateBasic and OnValidateBearer for the securitySchemes the spec declares, and Mock answers an operation that has no handler from the spec's own examples.

Demos

Two complete demos ship with sgcOpenAPI 2026.6, both hosted by the standalone pair, so no sgcWebSockets installation is required:

Upgrading

If you currently use TsgcWSServer_API_OpenAPI with sgcWebSockets, nothing changes. The class, its properties and its events are all preserved, and the implementation delegates to the shared engine. TsgcWSAPIServer_OpenAPI is the published descendant of that same class, so the only thing sgcOpenAPI changes is where the package comes from.

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.