6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

カバー株式会社Advent Calendar 2023

Day 15

OpenAPI 定義ファイルから自動生成された Flutter (Dart) の API クライアントの便利な使い方

Last updated at Posted at 2023-12-14

この記事はカバー株式会社 Advent Calendar 2023 15日目の記事になります。

カバー株式会社エンジニアのSです。よろしくお願いいたします。

前回の記事は @ys-cover による「カバー株式会社におけるAWS Control Towerの導入」でした。AWS Control Tower の導入を検討されている方にとって、とても興味深い内容となっておりますので、こちらの記事もぜひご覧ください。

はじめに

先日弊チームメンバーが作成した記事が公開されました。記事に対する反応で多かったのが API クライアントの自動生成の箇所でした。

API クライアントの自動生成には openapi-generatordart モジュールを利用しています。早期に API クライアント自動生成の仕組みを整えることで、クライアントチームは本質的な機能実装に注力可能になりました。

また API クライアントは Dart 標準の http ライブラリを用いて作成されるため、メンテナンスコストを低く抑えられ、拡張性も高く様々な要件に柔軟に対応可能でした。

本記事では openapi-generator の導入方法の詳細は省きつつ、生成された API クライアントの拡張方法や便利な使い方について具体例を交えて紹介します。

便利な使い方

openapi-generator 経由で自動生成された API クライアントは下記で初期化することで利用可能です。

import 'package:openapi/api.dart' as openapi;

final baseUrl = "https://api.example.com";
final apiClient = openapi.ApiClient(
  basePath: baseUrl,
);

言及がない限り、後続のコードブロック内に登場する apiClient は上記のものを指します。

共通リクエストヘッダを定義したい

下記の addDefaultHeader 関数で共通リクエストヘッダの定義が可能です。

// 共通リクエストヘッダの定義
apiClient
        ..addDefaultHeader('App-Version', '1.0.0')
        ..addDefaultHeader('Build-Number', '45')
        ..addDefaultHeader('Accept-Language', 'en-US,en;q=0.5');

動的に Authorization リクエストヘッダを定義したい

API クライアント初期化時、下記の authentication 引数の指定で可能です。

import 'package:openapi/api.dart' as openapi;

final baseUrl = "https://api.example.com";
final apiClient = openapi.ApiClient(
  basePath: baseUrl,
  authentication: ApiClientAuth(
    () async => authUser?.getIdToken(),
  ),
);

authentication 引数に指定する ApiClientAuth の内容は下記です。指定する値は、自動生成された抽象クラス Authentication を継承している必要があります。

openapi/client/lib/auth/authentication.dart
abstract class Authentication {
  Future<void> applyToParams(
    List<QueryParam> queryParams,
    Map<String, String> headerParams,
  );
}
api_client_auth.dart
import 'package:openapi/api.dart';

class ApiClientAuth implements Authentication {
  ApiClientAuth(this.tokenCaller);

  final Future<String?> Function() tokenCaller;

  @override
  Future<void> applyToParams(
    List<QueryParam> queryParams,
    Map<String, String> headerParams,
  ) async {
    final accessToken = await tokenCaller();

    if (accessToken != null) {
      headerParams['Authorization'] = 'Bearer $accessToken';
    }
  }
}

利用する API の Client を差し替えたい

例えば、リトライポリシーや通信時に割り込み処理を追加したい場合、標準 http ライブラリだけでは対応できません。対応の一つとして http ライブラリの Client を差し替える方法があります。

自動生成された API クライアント内部では、標準 http ライブラリの Client を利用しています。そのため、BaseClient を継承したクラスであれば差し替え可能です。

http_interceptor のような標準 http ライブラリとの連携が意識されているライブラリであれば Client を差し替え可能です。flutter pub add http_interceptor 実行後に下記で差し替え時の挙動確認が可能です。

import 'package:openapi/api.dart' as openapi;

final apiClient = openapi.ApiClient(
  basePath: baseUrl,
)..client = InterceptedClient.build(
  interceptors: [LoggingInterceptor()],
  retryPolicy: ApiClientRetryPolicy(),
);

interceptors 引数に指定する ApiClientInterceptor の内容は下記です。指定する値は InterceptorContract を継承している必要があります。

logging_interceptor.dart
import 'package:http_interceptor/http/interceptor_contract.dart';
import 'package:http_interceptor/models/request_data.dart';
import 'package:http_interceptor/models/response_data.dart';

class LoggingInterceptor implements InterceptorContract {
  @override
  Future<RequestData> interceptRequest({required RequestData data}) async {
    print(data.toString());
    return data;
  }

  @override
  Future<ResponseData> interceptResponse({required ResponseData data}) async {
    print(data.toString());
    return data;
  }
}

retryPolicy 引数に指定する ApiClientRetryPolicy の内容は下記です。指定する値は RetryPolicy を継承している必要があります。

logging_interceptor.dart
import 'package:http_interceptor/models/retry_policy.dart';

class ApiClientRetryPolicy extends RetryPolicy {
  @override
  int get maxRetryAttempts => 3;

  @override
  Future<bool> shouldAttemptRetryOnResponse(ResponseData response) async {
    if (500 <= response.statusCode) {
      return true;
    }

    return false;
  }
}

おわりに

弊チームでは本記事内で紹介した内容で API クライアントを利用しております。OpenAPI 定義ファイルから Flutter で利用可能な API クライアントの自動生成を検討されている方の参考になれば幸いです。

また、本記事では紹介しませんでしたが openapi-generatordart モジュールの導入を検討したくなった方のために、参考記事を後述の参考リンクに載せておきます。

次回は @mk-cover による「「笑い声」を分析・検証してみる」です。こちらも是非ご覧ください。

参考リンク

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?