課題: 最近の API のほとんどは OpenAPI 仕様を提供し、Delphi SDK は提供しない
Delphi でモダンな REST API を統合したことがあるなら、そのワークフローはおそらくこうだったはずです: ドキュメントを読み、TIdHTTP または TNetHTTPClient 周りのラッパーを手書きし、リクエストボディを文字列リテラルに貼り付け、TJSONObject でレスポンスをパースし、TDateTime シリアル化と戦い、エンドポイントごとに繰り返す。動きはしますがスケールしません。API には 80 エンドポイントがあり、8 個をラップし、ベンダーは翌月さらに 10 個追加し、ラッパーは腐ります。
OpenAPI(旧 Swagger)の約束は、ベンダーが機械可読な記述 — 1 つの YAML または JSON ファイル — を公開し、ジェネレーターがそれを選択した言語の型付きクライアントに変えるというものです。ほとんどの言語ではこれが極めてうまく動きます: openapi-generator と swagger-codegen は Python、TypeScript、Go、Java、C#、Rust など多数の慣用的クライアントを生成します。
Delphi では歴史的に話はそれほど滑らかではありませんでした。本稿では 2026 年時点の主要な 2 つの選択肢 — 長年存在するオープンソースの swagger-codegen Delphi ジェネレーターと eSeGeCe のネイティブ商用 sgcOpenAPI — を比較し、同じ入力仕様からそれぞれが何を生成するかを示します。
swagger-codegen と openapi-generator について
SmartBear 傘下となった Swagger プロジェクトは swagger-codegen を提供しています。2018 年のコミュニティフォーク後、並行プロジェクトの openapi-generator が多くの言語のデファクトスタンダードとなりました。両ツールとも Mustache テンプレートで多数のターゲット言語をサポートします。Delphi ジェネレーター(swagger-codegen の delphi、加えてコミュニティテンプレート)は存在しますが、これは常に時折のボランティアが保守する二級ターゲットでした。
執筆時点で、openapi-generator の Delphi テンプレートは古い Delphi バージョンではコンパイルされるコードを生成しますが、最新 Delphi(D11/D12/D13)では既知の問題があります: nullable フィールドの誤った扱い、より新しい RTL のための {$IFDEF} ガードの不足、OpenAPI 3.1 のサポートなし、ストリーミングレスポンスのサポートなし、特定の HTTP クライアントライブラリバージョンに依存するランタイム。Delphi ジェネレーターに対する長年の GitHub イシューのいくつかは何年も開いたままです。仕様により結果は異なります。
sgcOpenAPI について
sgcOpenAPI は eSeGeCe のネイティブ Delphi ツールです。OpenAPI 3.0(および現在は 3.1)仕様を解析し、sgc 命名規約に従う Delphi ユニットを出力します: クライアントは Tsgc[Api]Client、各スキーマオブジェクトは Tsgc[Api]_[Model]、操作ごとに 1 メソッド。生成されたコードはトランスポートに sgcWebSockets の TsgcHTTPComponentClient を使用するため、認証、再試行、TLS、HTTP/2 が無料で引き継がれます。
ジェネレーター自体が Delphi で書かれているため、Delphi ツールに埋め込んだり、コマンドラインから実行したり、ビルドスクリプトから呼び出したりできます。出力は sgcWebSockets を超えるランタイム依存のない単純な .pas ファイルです — JVM 不要、Node 不要、Python 不要、テンプレートエンジン不要。
機能比較
| 機能 | swagger-codegen / openapi-generator(Delphi ターゲット) | sgcOpenAPI |
|---|---|---|
| OpenAPI 2.0(Swagger) | あり | あり |
| OpenAPI 3.0 | あり(openapi-generator) | あり |
| OpenAPI 3.1 | 執筆時点で限定的 | あり |
| ランタイム要件 | ジェネレーター実行に Java 11+ | なし(ネイティブ Delphi exe) |
| ターゲット Delphi バージョン | D10.x ではベストエフォート、執筆時点で D11+ では頻繁に壊れる | D7 から D13 まで |
| HTTP バックエンド | 現行 Delphi テンプレートでは既定で Indy | sgcHTTPComponentClient(Indy / ICS / SChannel、加えて HTTP/2) |
| 非同期/ストリーミングレスポンス | 限定的 | あり |
| OAuth2 / API キー / ベアラー認証 | 部分的 | あり、ネイティブコンポーネント |
多態性(oneOf、allOf、discriminator) | 限定的 | あり |
| nullable フィールド | 一貫性なし | あり(TsgcNullable<T>) |
| ファイルアップロード/マルチパート | 部分的 | あり |
| サーバースタブ生成 | あり(主に他言語向け) | あり(sgcWebSocketHTTPServer による Delphi サーバースタブ) |
| ライセンス | オープンソース(Apache 2.0) | 商用 |
ワークフロー比較
swagger-codegen ワークフロー
- Java 11 以降をインストール。
openapi-generator-cliJAR をダウンロード。java -jar openapi-generator-cli.jar generate -i spec.yaml -g delphi -o ./outを実行。- 生成された
.pasファイルを IDE で開く。Indy ランタイムパスを追加。Delphi バージョン向けにコンパイル問題を修正。nullable の扱いをパッチ。仕様変更のたびに再実行。 - 生成されたクライアントは、テンプレートが出荷する別個のランタイムサポートユニットに依存します。そのユニットをプロジェクトと共に出荷します。
sgcOpenAPI ワークフロー
- sgcOpenAPI IDE を開くか、CLI を呼び出す:
sgcOpenAPI.exe -i spec.yaml -o ./out。 - 生成されたユニットを IDE で開く。sgcWebSockets を使用していれば既に持っている
TsgcHTTPComponentClientを使用します。 - 生成されたクライアントコンポーネントをフォームにドロップし、ベース URL と資格情報を設定し、型付きメソッドを呼び出す。
並列比較: 生成された GET /users/{id} メソッド
同じ OpenAPI 断片、両ツール、同一の操作。ヘルパー名と正確な書式は明瞭さのためにわずかにスタイル化されていますが、アプローチの違いは本物です。
swagger-codegen(Delphi ターゲット、簡略化)
function TUsersApi.GetUserById(const Id: Int64): TUser;
var
HttpRequest: TIdHTTP;
Path, Response: string;
JsonValue: TJSONValue;
begin
Path := StringReplace(BasePath + '/users/{id}', '{id}', IntToStr(Id), [rfReplaceAll]);
HttpRequest := TIdHTTP.Create(nil);
try
HttpRequest.Request.CustomHeaders.AddValue('Authorization', 'Bearer ' + FApiKey);
Response := HttpRequest.Get(Path);
finally
HttpRequest.Free;
end;
JsonValue := TJSONObject.ParseJSONValue(Response);
try
Result := TUser.Create;
Result.Id := (JsonValue as TJSONObject).GetValue<Int64>('id');
Result.Name := (JsonValue as TJSONObject).GetValue<string>('name');
// ... nullable fields handled inconsistently ...
finally
JsonValue.Free;
end;
end;
sgcOpenAPI
function TsgcUsersApiClient.GetUserById(const aId: Int64): TsgcUser;
var
vResponse: TsgcAPIResponse;
begin
Result := nil;
vResponse := DoGet(Format('/users/%d', [aId]));
Try
if vResponse.StatusCode = 200 then
Result := TsgcUser.FromJSON(vResponse.Content);
Finally
vResponse.Free;
End;
end;
// TsgcUser is generated with typed nullable fields:
// property Id: Int64 read FId write FId;
// property Name: string read FName write FName;
// property Email: TsgcNullable<string> read FEmail write FEmail;
// property CreatedAt: TDateTime read FCreatedAt write FCreatedAt;
//
// Authentication, retry, HTTP/2, TLS are configured on the
// underlying TsgcHTTPComponentClient once, not per-method.
注目すべき相違点:
- sgcOpenAPI はトランスポート(コンポーネントレベル設定)と操作(型付きメソッド)を分離するため、認証、再試行、TLS は一度だけ設定します。
- nullable フィールドは「不在」「null」「値」を区別する型付きラッパー
TsgcNullable<T>を使用 — OpenAPI 3.x のセマンティクスに一致します。 - 日付/時刻フィールドは、後でパースする文字列ではなく、適切な ISO 8601 パーサーを持つ本物の
TDateTimeを使用します。 - 生成されたクライアントは sgcWebSockets HTTP コンポーネントを使用し、サーバーが ALPN 経由でアドバタイズすれば HTTP/2 を透過的にサポートします。
サーバースタブ
両ツールともサーバー側コードを生成できますが、openapi-generator の Delphi サーバーテンプレートは執筆時点で最小限です。sgcOpenAPI は、操作ごとに 1 つの仮想メソッド、リクエスト検証、レスポンス整形、組み込みの OpenAPI エクスプローラーエンドポイントを備えた TsgcWebSocketHTTPServer ベースのハンドラーを生成します。単一の Delphi プロジェクトで API を公開し、Delphi クライアントで消費したい内部サービスでは、ラウンドトリップが非常に短くなります。
swagger-codegen が依然として正しい選択である場合
- すでに多数の言語にわたって
openapi-generatorに標準化しており、CI パイプラインに 1 ツールを置きたい。 - 仕様が小さく安定しており、OpenAPI 3.1 機能を必要としない。
- 生成された Delphi をターゲットバージョンに合わせてパッチするのに慣れている。
- ライセンス上の理由で商用ソフトウェアを使用できない。
sgcOpenAPI が実時間を節約する場合
- 現行 Delphi バージョン(D11 / D12 / D13)にいて、執筆時点で swagger-codegen の Delphi テンプレートががたついている。
- 仕様が OpenAPI 3.1、
oneOf/discriminator、nullable フィールド、ファイルアップロード、または非同期ストリーミングを使用している。 - すでに sgcWebSockets を使用しており、一貫したコンポーネントモデルと単一の HTTP スタックが欲しい。
- HTTP/2、TLS、OAuth2、再試行が、生成されたクライアントを書き直さずに「ただ動く」必要がある。
まとめ
OpenAPI からのコード生成は、ジェネレーターが仕様と Delphi バージョンに追従する限り — プロジェクトの生涯にわたって積み上がる生産性の勝利の 1 つです。swagger-codegen / openapi-generator は優れたマルチ言語ツールですが、その Delphi ターゲットは歴史的にベストエフォートとして扱われてきました。sgcOpenAPI は、Java 依存も手動パッチサイクルもなしに、コンパイル可能で慣用的、フル機能の OpenAPI 3.0 / 3.1 クライアント(およびサーバースタブ)を提供する、焦点を絞ったネイティブ Delphi の代替案です。すでに REST API を統合している多くの Delphi チームにとって、ベンダーが仕様を更新する最初のときに元が取れます。