kawa.dev

BlogSlide
BigQueryにアクセスするServiceAccountを作成BigQueryをDartから呼ぶBigQueryを実際に叩く

DartからBigQueryのクエリを叩く方法

2021-09-04

Dart

/

BigQuery

FirestoreなどのNoSQLを使用しているとBigQueryの結果をレポートとして表示したいケースがあると思います.

Dartでバックエンドを構築している場合の,BigQueryのクエリを叩く方法を紹介します.

実際のコードを公開しています.お試しはこちらからどうぞ!

https://github.com/kawa1214/dart-bigquery

BigQueryにアクセスするServiceAccountを作成

GCPのIAMのサービスアカウントから

  • BigQuery ジョブユーザ
  • BigQuery データ閲覧者

のロールを持ったサービスアカウントを作成します.

作成したサービスアカウントのキーから新しい鍵を作成し,JSON形式で保存します.

service-account-json-1
service-account-json-2

サービスアカウントの情報を環境変数として定義 Dockerで環境変数に追加します.

.env

PROJECT_ID= PRIVATE_KEY_ID= PRIVATE_KEY= CLIENT_EMAIL= CLIENT_ID=

DockerFile

# Official Dart image: https://hub.docker.com/_/dart # Specify the Dart SDK base image version using dart:<version> (ex: dart:2.12) FROM dart:stable AS build # Resolve app dependencies. WORKDIR /app COPY pubspec.* ./ RUN dart pub get # Copy app source code and AOT compile it. COPY . . # Ensure packages are still up-to-date if anything has changed RUN dart pub get --offline RUN dart compile exe bin/server.dart -o bin/server # Build minimal serving image from AOT-compiled `/server` and required system # libraries and configuration files stored in `/runtime/` from the build stage. FROM scratch COPY --from=build /runtime/ / COPY --from=build /app/bin/server /app/bin/ # Start server. EXPOSE 8080 CMD ["/app/bin/server"]

dockr-compose.yaml

version: "3.4" services: server: build: context: . dockerfile: Dockerfile image: server restart: always container_name: server ports: - "8080:8080" env_file: - .env

lib/constants/environment_variables.dart に環境変数のKeyを定義します.

const projectIdMapKey = 'PROJECT_ID'; const privateKeyIdMapKey = 'PRIVATE_KEY_ID'; const privateKeyMapKey = 'PRIVATE_KEY'; const clientEmailMapKey = 'CLIENT_EMAIL'; const clientIdMapKey = 'CLIENT_ID';

dart側からはfinal env = Platform.environment; で呼び出すことができます.

import '../constants/environment_variables.dart' as environment_variables; final env = Platform.environment; env[environment_variables.projectIdMapKey] // これでプロジェクトIDを参照することが出来ます

BigQueryをDartから呼ぶ

BigQueryAPIをwrapperしているパッケージを追加します

dependencies: googleapis: ^4.0.0 // APIのwrapper googleapis_auth: ^1.1.0 // サービスアカウントの認証で使います

サービスアカウントの認証はgoogleapis_authを使用します.

final env = Platform.environment; final credentials = googleapis_auth.ServiceAccountCredentials.fromJson({ 'type': 'service_account', 'project_id': env[environment_variables.projectIdMapKey], 'private_key_id': env[environment_variables.privateKeyIdMapKey], 'private_key': '-----BEGIN PRIVATE KEY-----\n${env[environment_variables.privateKeyMapKey]}\n-----END PRIVATE KEY-----\n', 'client_email': env[environment_variables.clientEmailMapKey], 'client_id': env[environment_variables.clientIdMapKey], }); final authClient = await googleapis_auth.clientViaServiceAccount( credentials, [googleapis.BigqueryApi.bigqueryScope], );

googleapisを用いてBigQueryのAPIを叩きます.

デフォルトではuseLegacySqlがfalseになっているため,標準SQLを使う場合はfalse にします.

タイムアウトはデフォルトで10秒なので,使用ケースに合わせて適切な値に設定します.

final queryResponse = await googleapis.BigqueryApi(authClient).jobs.query( googleapis.QueryRequest( useLegacySql: false, query: query, timeoutMs: 20000, ), projectId, );

BigQueryを叩くためのクライアントクラスは,最終的に以下のようになります.

import 'dart:io'; import 'package:googleapis/bigquery/v2.dart' as googleapis; import 'package:googleapis_auth/auth_io.dart' as googleapis_auth; import 'package:googleapis_auth/googleapis_auth.dart' as googleapis_auth; import '../constants/environment_variables.dart' as environment_variables; class BigQueryClient { BigQueryClient(this.projectId, this.authClient); final String projectId; final googleapis_auth.AutoRefreshingAuthClient authClient; Future<googleapis.QueryResponse> query(String query) async { try { final queryResponse = await googleapis.BigqueryApi(authClient).jobs.query( googleapis.QueryRequest( useLegacySql: false, query: query, timeoutMs: 50000, ), projectId, ); return queryResponse; } catch (e, s) { stderr.writeAll({ 'Error': e, 'StackTrace': s, }.entries); rethrow; } } static Future<BigQueryClient> getInstance() async { final env = Platform.environment; final credentials = googleapis_auth.ServiceAccountCredentials.fromJson({ 'type': 'service_account', 'project_id': env[environment_variables.projectIdMapKey], 'private_key_id': env[environment_variables.privateKeyIdMapKey], 'private_key': '-----BEGIN PRIVATE KEY-----\n${env[environment_variables.privateKeyMapKey]}\n-----END PRIVATE KEY-----\n', 'client_email': env[environment_variables.clientEmailMapKey], 'client_id': env[environment_variables.clientIdMapKey], }); final authClient = await googleapis_auth.clientViaServiceAccount( credentials, [googleapis.BigqueryApi.bigqueryScope], ); return BigQueryClient( env[environment_variables.projectIdMapKey]!, authClient, ); } }

実際にBigQueryClient を呼び出す処理は次のようになります.

エラーがある場合 レスポンスの行が0の場合 でエラーハンドリングをしています.

import 'dart:io'; import 'package:shelf/shelf.dart'; import 'package:shelf/shelf_io.dart' as shelf_io; import 'package:dart_bigquery/clients/big_query_client.dart'; void main() async { final handler = Pipeline().addHandler(_queryRequest); final server = await shelf_io.serve(handler, InternetAddress.anyIPv4, 8080); server.autoCompress = true; print('Serving at http://${server.address.host}:${server.port}'); } Future<Response> _queryRequest(Request request) async { final client = await BigQueryClient.getInstance(); final res = await client.query(''' select * from table '''); final errors = res.errors ?? []; if (errors.isNotEmpty) { throw Exception(errors); } final tableRows = res.rows; if (tableRows == null) { throw Exception('Incorrect query'); } final data = tableRows.map((e) => e.f!.map((e) => e.toJson())).toList(); return Response.ok(data); }

BigQueryを実際に叩く

docker-compose up -d

で立ち上げます.

http://0.0.0.0:8080を叩くとクエリ結果が返ります.

最小構成のコードを公開しています.実際に試してみたい方はこちらからどうぞ!

https://github.com/kawa1214/dart-bigquery