以下のコマンドで初期化
firebase init dataconnect
GraphQLのファイル群
dataconnect/
├── dataconnect.yaml
├── default-connector
│ ├── connector.yaml
│ ├── mutations.gql
│ └── queries.gql
└── schema
└── schema.gql
gqlのコメントアウトを外す
VSCodeでプラグインを入れてから保存するとdartのファイルができる
'generated/default_connector.dart';
Userをログインなしで一覧取得、追加、更新には以下が必要だった。
queries.gql
query ListUsers @auth(level: PUBLIC) {
users {
uid
name
address
}
}
mutations.gql
# mutation CreateUser($name: String!, $address: String!) @auth(level: USER) {
mutation CreateUser($name: String!, $address: String!) @auth(level: PUBLIC) {
# mutation UpdateUser($uid: String!, $name: String!, $address: String!) @auth(level: USER) {
mutation UpdateUser($uid: String!, $name: String!, $address: String!) @auth(level: PUBLIC) {
connector.yaml
connectorId: "default-connector"
authMode: "PUBLIC"
## ## Here's an example of how to add generated SDKs.
## ## You'll need to replace the outputDirs with ones pointing to where you want the generated code in your app.
# generate:
# javascriptSdk:
# outputDir: <Path where you want the generated SDK to be written to, relative to this file>
# package: "@firebasegen/my-connector"
# packageJSONDir: < Optional. Path to your Javascript app's package.json>
# swiftSdk:
# outputDir: <Path where you want the generated SDK to be written to, relative to this file>
# kotlinSdk:
# outputDir: <Path where you want the generated SDK to be written to, relative to this file>
generate:
dartSdk:
outputDir: "../../lib/generated" # 出力先をFlutterプロジェクトに合わせて調整
package: "com.example.yourproject" # あなたのパッケージ名に置き換え(なくても動くが推奨)
編集終わったら、デプロイ
firebase deploy --only dataconnect
試行錯誤した main.dart
import 'package:firebase_app_check/firebase_app_check.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_data_connect/firebase_data_connect.dart';
import 'package:flutter/material.dart';
import 'firebase_options.dart';
// 自動生成されたファイル
import 'generated/default_connector.dart';
late final FirebaseDataConnect dataConnect;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
// Firebase App Check API をGCPで有効にしないとダメ
// AppCheck をデバッグモードで有効にする(iOS/Android両対応)
await FirebaseAppCheck.instance.activate(
appleProvider: AppleProvider.debug,
);
await FirebaseAuth.instance.signInAnonymously();
print('UID: ${FirebaseAuth.instance.currentUser?.uid}');
final connectorConfig = DefaultConnectorConnector.connectorConfig;
dataConnect =
FirebaseDataConnect.instanceFor(connectorConfig: connectorConfig);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'User CRUD',
theme: ThemeData(useMaterial3: true),
home: const UserScreen(),
);
}
}
class UserScreen extends StatefulWidget {
const UserScreen({super.key});
@override
State<UserScreen> createState() => _UserScreenState();
}
class _UserScreenState extends State<UserScreen> {
final _nameController = TextEditingController();
final _addressController = TextEditingController();
List<ListUsersUsers> _users = [];
String _status = '';
@override
void initState() {
super.initState();
_fetchUsers();
}
Future<void> _fetchUsers() async {
try {
final builder = ListUsersVariablesBuilder(dataConnect);
final result = await builder.execute();
final users = result.data.users ?? [];
setState(() {
_users = users;
_status = '取得成功 (${users.length}件)';
});
} catch (e) {
print(e);
setState(() {
_status = '取得エラー: $e';
});
}
}
Future<void> _createUser() async {
final name = _nameController.text.trim();
final address = _addressController.text.trim();
if (name.isEmpty || address.isEmpty) return;
try {
final builder = CreateUserVariablesBuilder(
dataConnect,
name: name,
address: address,
);
final result = await builder.execute();
_nameController.clear();
_addressController.clear();
await _fetchUsers();
setState(() => _status = '追加成功');
} on DataConnectOperationError catch (e) {
print('❌ DataConnectOperationError 発生');
print('コード: ${e.code}');
print('メッセージ: ${e.message}');
// エラー詳細を列挙
for (final error in e.response.errors) {
print('🚨 エラー: ${error.message}');
print('🔍 パス: ${error.path.map((p) => p.toString()).join(" > ")}');
}
// 最初のエラーを UI に反映
final firstMessage = e.response.errors.isNotEmpty
? e.response.errors.first.message
: '不明なエラー';
setState(() => _status = '追加失敗: $firstMessage');
} catch (e, stack) {
print('❗️ 例外発生: ${e.runtimeType}');
print('内容: $e');
print('StackTrace: $stack');
setState(() => _status = '追加失敗(例外): $e');
}
}
Future<void> _upsertUser() async {
final name = _nameController.text.trim();
final address = _addressController.text.trim();
final uid = FirebaseAuth.instance.currentUser?.uid;
if (uid == null) {
print('❌ UIDが取得できませんでした');
return;
}
try {
// 1. すでに登録されているか確認
final result = await ListUsersVariablesBuilder(dataConnect).execute();
final user = result.data.users.firstWhere(
(u) => u.uid == uid,
// orElse: () => null,
);
if (user == null) {
// 2. 登録されていなければ INSERT
print('🆕 ユーザーが見つからないので新規登録します');
await CreateUserVariablesBuilder(
dataConnect,
name: name,
address: address,
).execute();
} else {
// 3. すでにあるなら UPDATE
print('🔄 既存ユーザーを更新します');
await UpdateUserVariablesBuilder(
dataConnect,
uid: uid,
name: name,
address: address,
).execute();
}
print('✅ 処理完了');
await _fetchUsers();
} catch (e) {
print('❌ 例外: $e');
}
}
// Future<void> _deleteMyUser() async {
// final uid = FirebaseAuth.instance.currentUser?.uid;
// if (uid == null) return;
//
// final builder = UserDeleteVariablesBuilder(dataConnect);
// final result = await builder.execute(uid: uid);
//
// if (result.hasError) {
// setState(() => _status = '削除失敗: ${result.error}');
// } else {
// _fetchUsers();
// setState(() => _status = '削除成功');
// }
// }
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('ユーザー一覧 / 追加 / 削除')),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
children: [
TextField(
controller: _nameController,
decoration: const InputDecoration(labelText: '名前')),
TextField(
controller: _addressController,
decoration: const InputDecoration(labelText: '住所')),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(onPressed: _upsertUser, child: const Text('追加')),
// ElevatedButton(
// onPressed: _deleteMyUser, child: const Text('自分を削除')),
],
),
const SizedBox(height: 16),
Text('ステータス: $_status'),
const Divider(),
const Text('ユーザー一覧', style: TextStyle(fontWeight: FontWeight.bold)),
Expanded(
child: ListView.builder(
itemCount: _users.length,
itemBuilder: (_, i) {
final user = _users[i];
return ListTile(
title: Text(user.name ?? 'No Name'),
subtitle: Text(user.address ?? ''),
);
},
),
)
],
),
),
);
}
}
Firebase App Check API をGCPで有効にしないとダメだった
あとは、
のAppCheckから
以下をして、

Xcode でプロジェクトを開く(ios/Runner.xcworkspace)
メニューから Product → Scheme → Edit Scheme… を選ぶ
左の「Run」を選択
上部タブから「Arguments」を開く
Arguments Passed on Launch に → -FIRDebugEnabled を追加
「Close」で保存して完了!
Xcode から実行 flutter run や Android Studio からの起動では -FIRDebugEnabled は有効になりません
Xcodeで以下のようなログが出る。
Firebase App Check Debug Token: xxxxxxx-xx-xx-xxx-xxxx
名前をテキトーにつけて、Schemeの編集で保存する。
