sgcOpenAPI vs Swagger Codegen — Delphi クライアントをより速く生成

· レビュー

課題: 最近の API のほとんどは OpenAPI 仕様を提供し、Delphi SDK は提供しない

Delphi でモダンな REST API を統合したことがあるなら、そのワークフローはおそらくこうだったはずです: ドキュメントを読み、TIdHTTP または TNetHTTPClient 周りのラッパーを手書きし、リクエストボディを文字列リテラルに貼り付け、TJSONObject でレスポンスをパースし、TDateTime シリアル化と戦い、エンドポイントごとに繰り返す。動きはしますがスケールしません。API には 80 エンドポイントがあり、8 個をラップし、ベンダーは翌月さらに 10 個追加し、ラッパーは腐ります。

OpenAPI(旧 Swagger)の約束は、ベンダーが機械可読な記述 — 1 つの YAML または JSON ファイル — を公開し、ジェネレーターがそれを選択した言語の型付きクライアントに変えるというものです。ほとんどの言語ではこれが極めてうまく動きます: openapi-generatorswagger-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 テンプレートでは既定で IndysgcHTTPComponentClient(Indy / ICS / SChannel、加えて HTTP/2)
非同期/ストリーミングレスポンス限定的あり
OAuth2 / API キー / ベアラー認証部分的あり、ネイティブコンポーネント
多態性(oneOfallOfdiscriminator限定的あり
nullable フィールド一貫性なしあり(TsgcNullable<T>
ファイルアップロード/マルチパート部分的あり
サーバースタブ生成あり(主に他言語向け)あり(sgcWebSocketHTTPServer による Delphi サーバースタブ)
ライセンスオープンソース(Apache 2.0)商用

ワークフロー比較

swagger-codegen ワークフロー

  1. Java 11 以降をインストール。
  2. openapi-generator-cli JAR をダウンロード。
  3. java -jar openapi-generator-cli.jar generate -i spec.yaml -g delphi -o ./out を実行。
  4. 生成された .pas ファイルを IDE で開く。Indy ランタイムパスを追加。Delphi バージョン向けにコンパイル問題を修正。nullable の扱いをパッチ。仕様変更のたびに再実行。
  5. 生成されたクライアントは、テンプレートが出荷する別個のランタイムサポートユニットに依存します。そのユニットをプロジェクトと共に出荷します。

sgcOpenAPI ワークフロー

  1. sgcOpenAPI IDE を開くか、CLI を呼び出す: sgcOpenAPI.exe -i spec.yaml -o ./out
  2. 生成されたユニットを IDE で開く。sgcWebSockets を使用していれば既に持っている TsgcHTTPComponentClient を使用します。
  3. 生成されたクライアントコンポーネントをフォームにドロップし、ベース 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.

注目すべき相違点:

サーバースタブ

両ツールともサーバー側コードを生成できますが、openapi-generator の Delphi サーバーテンプレートは執筆時点で最小限です。sgcOpenAPI は、操作ごとに 1 つの仮想メソッド、リクエスト検証、レスポンス整形、組み込みの OpenAPI エクスプローラーエンドポイントを備えた TsgcWebSocketHTTPServer ベースのハンドラーを生成します。単一の Delphi プロジェクトで API を公開し、Delphi クライアントで消費したい内部サービスでは、ラウンドトリップが非常に短くなります。

swagger-codegen が依然として正しい選択である場合

sgcOpenAPI が実時間を節約する場合

まとめ

OpenAPI からのコード生成は、ジェネレーターが仕様と Delphi バージョンに追従する限り — プロジェクトの生涯にわたって積み上がる生産性の勝利の 1 つです。swagger-codegen / openapi-generator は優れたマルチ言語ツールですが、その Delphi ターゲットは歴史的にベストエフォートとして扱われてきました。sgcOpenAPI は、Java 依存も手動パッチサイクルもなしに、コンパイル可能で慣用的、フル機能の OpenAPI 3.0 / 3.1 クライアント(およびサーバースタブ)を提供する、焦点を絞ったネイティブ Delphi の代替案です。すでに REST API を統合している多くの Delphi チームにとって、ベンダーが仕様を更新する最初のときに元が取れます。