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:
- An embedded HTTP server (Indy-based) with the usual
Bindings,PortandActiveproperties. - The OpenAPI engine: spec parsing, path-template routing with
{paramName}segments, JSON-Schema validation, CORS, exception handling. - Two auto-served endpoints: the spec at
/openapi.jsonand a Swagger UI at/docs. Both are on by default and can be toggled inOpenAPIOptions.Endpoint.
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:
- Demos/30.Server/01.CodeFirst — a Task Manager API defined entirely with attributes on a Delphi class.
- Demos/30.Server/02.SpecFirst — the classic Petstore example, served from
petstore.json.
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.
