OpenAPI Server for Delphi

TsgcWSAPIServer_OpenAPI serves the OpenAPI 3.x document you load, matches every incoming request against it, validates the request before your handler runs, and publishes the document and a Swagger UI page from the same port. One Delphi component, attached to a TsgcHTTPServer.

OpenAPI 3.0 & 3.1
HTTP/2 + TLS 1.3
Swagger UI at /docs
Spec-first or Code-first

TsgcWSAPIServer_OpenAPI

One Delphi component that turns an OpenAPI document into a running, validated, self-documenting REST server.

Component class

TsgcWSAPIServer_OpenAPI, declared in sgcWebSocket_Server_API_OpenAPI

Host server

Assign Server a TsgcHTTPServer, a TsgcHTTPRESTServer or a TsgcWebSocketHTTPServer. The host owns the port, the bindings and TLS.

Spec format

OpenAPI 3.0 and 3.1 documents, read as JSON by LoadFromFile and LoadFromString

Two workflows

Spec-first from a document you already have, or code-first from an attributed Delphi class. Code-first needs Delphi XE7 or later.

Edition

Ships with sgcOpenAPI. Inside sgcWebSockets it belongs to the Enterprise edition, on the SGC OpenAPI palette page.

Built-in endpoints

/openapi.json for the document and /docs for Swagger UI, both switched on in OpenAPIOptions.Endpoint

Spec-first or Code-first, You Choose

The same component runs in either mode. Start from a JSON contract, or describe the API in Delphi and let the scanner generate the document for you.

1. Spec-first

Load petstore.json with LoadFromFile, dispatch on the operation id inside OnRequest, and start serving. Routing, path and query parameter binding and validation all come from the contract, so you only write the business logic.

Best for: teams with a shared design contract, API-led integration, or polyglot back-ends where the spec is the source of truth.

2. Code-first

Annotate a plain Delphi class with sgcServiceContract, sgcRoute, sgcHttpGet and the sgcFromPath / sgcFromQuery / sgcFromBody parameter attributes. TsgcOpenAPICodeFirstScanner.GenerateSpec builds the OpenAPI document from the class RTTI, you hand it to LoadFromString, and the same /openapi.json endpoint publishes it.

Best for: rapid prototyping, internal services, or porting an existing TIdHTTPServer / DataSnap REST surface to a self-documenting API.

A Working Server in 20 Lines

Create the component, load a document, attach it to an HTTP server. That is the entire setup.

Delphi
uses
  sgcHTTP_Server, sgcHTTP_OpenAPI_Server, sgcWebSocket_Server_API_OpenAPI;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FServer := TsgcHTTPServer.Create(Self);
  FServer.Port := 8080;

  FOpenAPI := TsgcWSAPIServer_OpenAPI.Create(Self);
  FOpenAPI.LoadFromFile('petstore.json');      // any OpenAPI 3.x document
  FOpenAPI.OpenAPIOptions.Endpoint.ServeSpec := True;
  FOpenAPI.OpenAPIOptions.Endpoint.ServeSwaggerUI := True;
  FOpenAPI.OnRequest := OpenAPIRequest;
  FOpenAPI.Server := FServer;                // Server is the switch, there is no Active

  FServer.Active := True;
end;

// one event, dispatched by operation id
procedure TForm1.OpenAPIRequest(Sender: TObject;
  const aOperationId: string;
  const aContext: TsgcOpenAPIServerContext;
  var Handled: Boolean);
begin
  Handled := True;
  if aOperationId = 'getPetById' then
    aContext.RespondJSON(200, FPets.Values[aContext.PathParamAsString('petId')])
  else
    Handled := False;
end;

What you get out of the box: GET /pets/{petId} reaches the handler above with aOperationId set to getPetById, GET /openapi.json returns the document you loaded, GET /docs opens Swagger UI. OpenAPIOptions.Endpoint.BasePath moves the whole surface under a prefix, and TLS and HTTP/2 come from the host server.

Parameters declared in the OpenAPI document are read and converted through a single typed context. With validation on, a wrong type is answered with 400 Bad Request before your handler runs.

Delphi
// spec snippet
//   /pets:
//     get:
//       operationId: listPets
//       parameters:
//         - name: limit       in: query    schema: { type: integer, maximum: 100 }
//         - name: status      in: query    schema: { type: string, enum: [available, pending, sold] }
//         - name: X-Tenant-Id in: header   required: true

procedure TForm1.HandleListPets(const aContext: TsgcOpenAPIServerContext);
var
  vLimit:  Integer;
  vStatus: string;
  vTenant: string;
begin
  vLimit  := aContext.QueryParamAsInteger('limit', 20);        // default 20
  vStatus := aContext.QueryParamAsString ('status', 'available');
  vTenant := aContext.HeaderValue        ('X-Tenant-Id');   // required in the spec

  aContext.RespondJSON(200, PetRepo.List(vTenant, vStatus, vLimit));
end;

Schema Validation Before Your Handler Runs

Every incoming request is checked against the schemas the document declares. A failure is answered with an RFC 7807 style problem document listing each error, and never reaches your handler unless you say so.

What is checked

type, required, properties and additionalProperties, enum and const, minLength / maxLength, pattern, minimum / maximum with their exclusive forms, multipleOf, items, minItems / maxItems, uniqueItems, nullable, not, and oneOf / anyOf / allOf. The format keyword is enforced for date, date-time, email, ipv4, uri and uuid.

Choose the scope

Validation.ValidateRequest is the master switch and on its own validates every scope. Narrow it with ValidateRequestBody, ValidateQueryParams, ValidatePathParams, ValidateHeaderParams and ValidateCookieParams. EnforceRequired stays on whichever scope you pick.

Have the last word

OnValidationError hands you the operation id and the full list of failures. Its Continue flag arrives as False, so the request is rejected unless you deliberately set it to True. After a load, Validation.Warnings names every schema keyword the document uses that is not enforced, so an empty list means nothing went unchecked.

JSON, the 400 the engine writes
{
  "type":   "about:blank",
  "title":  "Bad Request",
  "status": 400,
  "detail": "Request validation failed",
  "errors": [
    "/email: invalid email format",
    "/age: must be <= 120",
    "/status: value not in enum"
  ]
}

Auth Schemes Driven by the Spec

Set Security.EnforceSecurity and the securitySchemes the document declares are applied to incoming requests. You write the credential lookup, the component parses the request and answers 401 or 403 when the lookup says no.

API Key

Read from a header, a query parameter or a cookie, whichever the scheme declares. OnValidateAPIKey receives the scheme, name, location and key, and answers through Valid.

HTTP Basic

The Authorization header is parsed for you. OnValidateBasic receives the user and the password and answers through Valid. Credentials are never written to the log.

Bearer and JWT

Security.JWTSecret verifies the token. An HMAC secret is used as it stands, a value containing -----BEGIN is treated as a PEM public key. ValidateExpiration, Issuer and Audience check the claims.

Your own verifier

Leave JWTSecret empty and the token is only checked for presence, so OnValidateBearer can hand it to your own token service and answer through Valid.

401 or 403

A request that fails is answered 401, or 403 when it authenticated and fell short only on scope. OnAuthenticate runs first and rejects with 401 the moment you clear Authenticated.

Mock before the code exists

Mock.Enabled answers an operation that has no handler from the document's own examples and schemas, with Mock.StatusCode, so a front end team can work while the implementation is written.

Delphi, bearer token verified by your own code
FOpenAPI.OpenAPIOptions.Security.EnforceSecurity := True;
FOpenAPI.OpenAPIOptions.Security.JWTSecret := GetSecretFromEnvironment;
FOpenAPI.OpenAPIOptions.Security.ValidateExpiration := True;
FOpenAPI.OpenAPIOptions.Security.Issuer   := 'https://auth.example.com';
FOpenAPI.OpenAPIOptions.Security.Audience := 'api.example.com';
FOpenAPI.OnValidateBearer := OpenAPIValidateBearer;

procedure TForm1.OpenAPIValidateBearer(Sender: TObject;
  const aToken: string;
  const aContext: TsgcOpenAPIServerContext; var Valid: Boolean);
begin
  Valid := MyTokenService.Verify(aToken);
end;

Swagger UI Embedded

No external dependency, no Node.js, no documentation build in the deployment pipeline. The component writes the page itself and it reads the document your server is actually serving.

/openapi.json

The document you loaded, served when Endpoint.ServeSpec is on. Always in step with what the server actually routes. Point any client generator at this URL, sgcOpenAPI included.

/docs

The interactive Swagger UI page, served when Endpoint.ServeSwaggerUI is on. Try operations, browse schemas, read the examples, all fed by your own running server.

Pinned, or fully offline

The page loads its CSS and JavaScript from a public CDN by default. Endpoint.SwaggerUIBaseURL pins a version, and Endpoint.SwaggerUIAssetsPath serves swagger-ui.css and swagger-ui-bundle.js from a local folder, so an air-gapped machine works too.

Everything Lives Under OpenAPIOptions

Five persistent sub-objects, all visible in the Object Inspector, all assignable at run time.

Endpoint

BasePath prefixes every route and both built-in endpoints. ServeSpec and ServeSwaggerUI toggle them. SpecFile is loaded lazily, on the first request that is neither of the two, so use LoadFromFile when the document must be complete from the very first call.

Validation

ValidateRequest plus the five scope switches, and EnforceRequired. Warnings reports, after each load, the schema keywords the document uses that this validator does not enforce.

CORS

Enabled, AllowOrigins, AllowHeaders and AllowMethods. The engine stamps the answers on the paths its document owns, so give the host server the same values for the paths it owns.

Security

EnforceSecurity, JWTSecret, ValidateExpiration, Issuer and Audience. Everything the built-in checks cannot decide reaches OnValidateAPIKey, OnValidateBasic or OnValidateBearer.

Mock

Enabled and StatusCode. An operation with no handler is answered from the document's own examples and schemas, so the contract is callable before the implementation exists.

Not Implemented, on purpose

Leave Handled at False and the engine answers 501 Not Implemented naming the operation, rather than a 404 that looks like a routing mistake.

One HTTP Server, Many Surfaces

TsgcWSAPIServer_OpenAPI attaches to the same sgcWebSockets HTTP server that hosts your WebSocket endpoints, AI/LLM streams and static files. One port, one TLS certificate, one logging stream.

Server is the switch

There is no Active property. Assigning Server attaches the component, setting it to nil detaches it, both while the host server keeps running. Detached, the paths its document owns fall straight through to your ordinary handler.

It never takes the server over

Each request is offered to the component first, and it answers only the paths its document declares. Everything else reaches OnCommandGet as before, so a contract-first section lives beside handwritten routes and static content from DocumentRoot, all on one port.

The host's TLS and HTTP/2

The port, the bindings, the certificate and the HTTP/2 negotiation belong to the host server, so the REST surface inherits them unchanged. Attach it to a TsgcHTTPRESTServer and the CORS, metrics, health and tenancy of that server apply too.

Typical Deployments

Public REST APIs

Versioned, contract-tested, with auto-generated SDKs that your customers can download from /openapi.json.

Internal microservices

Service-to-service contracts that survive refactors — the spec is the integration test.

Industrial / IoT gateways

Edge devices exposing a documented REST control plane plus an MQTT or WebSocket telemetry surface from the same Delphi binary.

Webhook receivers

Each provider's webhook payload becomes a typed Pascal record — Stripe, GitHub, Twilio, Slack — with validation and idempotency baked in.

Legacy modernisation

Wrap an old DataSnap or RemObjects back-end behind a clean OpenAPI surface without rewriting the business logic.

BFF (Backend-for-Frontend)

Aggregate two or three upstream APIs behind one consumer-shaped spec — your SPA or mobile app talks to a single, typed endpoint.

Pairs With

OpenAPI Parser

Load any external spec into the same model the server uses — same validation, same type system, same security primitives.

Pre-built cloud SDKs

1,195+ generated SDKs for AWS, Azure, GCP, Stripe, GitHub, Kubernetes and more — your server can call any of them with the same component family.

sgcWebSockets

WebSocket, MQTT, AMQP, WebRTC, AI/LLM, IoT — everything the HTTP server can host alongside your REST surface.

sgcSign

Sign request and response bodies with XAdES / PAdES / CAdES for regulated industries — eIDAS-grade integrity on every operation.

Best value: All-AccessEvery eSeGeCe product, Premium Support included, from €1,059/year.
See All-Access pricing

Build Your First OpenAPI Server in Minutes

Download the free trial. The full server, both UIs, every auth scheme — no feature limits, no time bomb during evaluation.