Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
React Native + Firestoreで運用しているアプリをRESTful APIからGraphQLに移行した話
Search
Yohei Iino
December 15, 2020
Programming
0
92
React Native + Firestoreで運用しているアプリをRESTful APIからGraphQLに移行した話
ペペロミアでRESTful APIからGraphQLに移行した話をまとめました。
■ ペペロミア
https://peperomia.app/
Yohei Iino
December 15, 2020
Tweet
Share
More Decks by Yohei Iino
See All by Yohei Iino
T3 Stack(応用編: Next Auth & SSRの実装紹介)
wheatandcat
1
31
App Routerの紹介
wheatandcat
0
17
Flutter × GraphQLでアプリを作ってみる
wheatandcat
0
42
T3 Stack + Supabaseでアプリを作ってみる
wheatandcat
0
85
Supabaseの紹介
wheatandcat
0
89
tRPCの紹介
wheatandcat
0
73
Plasmoの紹介
wheatandcat
0
48
Playwrightの紹介
wheatandcat
0
110
Vitestの紹介
wheatandcat
1
82
Other Decks in Programming
See All in Programming
Milestoner
bkuhlmann
1
400
Doctrine ORMでValue Objectを扱う方法4選 #phpstudy / 4 ways to handle Value Objects with Doctrine ORM
77web
4
110
From Spring Boot 2 to Spring Boot 3 with Java 21 and Jakarta EE
ivargrimstad
0
1.1k
Folding Cheat Sheet #1
philipschwarz
PRO
0
210
HUIT新歓2024「競技プログラミング、やってみませんか?」
slephy2784
1
250
データアナリストが行うDatabricksを活用したETLの自動化事例
shinoa
0
250
ログラスを支える設計標準について / loglass-design-standards
urmot
10
2.1k
Changed Rules: Architectures with Lightweight Stores
manfredsteyer
PRO
0
230
1인 개발자로 행복하게 살기 - GDG 송도 헬로월드 2024
benjaminkim
1
5.6k
Netty Chicago Java User Group 2024-04-17
sullis
0
120
Folding Cheat Sheet #3
philipschwarz
PRO
0
110
GitHub Actionsで泣かないためにやっておきたい設定 / Recommended GHA settings to avoid crying
pinkumohikan
3
490
Featured
See All Featured
WebSockets: Embracing the real-time Web
robhawkes
59
7k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
76
41k
Designing the Hi-DPI Web
ddemaree
276
33k
Product Roadmaps are Hard
iamctodd
43
9.7k
Making the Leap to Tech Lead
cromwellryan
123
8.5k
Ruby is Unlike a Banana
tanoku
96
10k
The Illustrated Children's Guide to Kubernetes
chrisshort
29
46k
Design by the Numbers
sachag
274
18k
Producing Creativity
orderedlist
PRO
336
39k
What's in a price? How to price your products and services
michaelherold
237
11k
StorybookのUI Testing Handbookを読んだ
zakiyama
11
4.6k
Pencils Down: Stop Designing & Start Developing
hursman
117
11k
Transcript
React Native + Firestore で運⽤しているアプリを RESTful API から GraphQL に移⾏した話
2020/12/17 @wheatandcat
⾃⼰紹介 wheatandcat フリーランスのエンジニア React Native / Go / Nuxt.js /
Firebase 周りが得意です
ペペロミアについて React Native(Expo)で作成しているライフログ アプリです。 Apple Store / Google Play Storeで配信中
& 全コードGitHubで公開中
今回やったこと !
アプリのv2からv3へバージョンアップで、 API実装をRESTful APIからGraphQLに完全移⾏しました。
v2 の構成 項⽬ ログイン前 ログイン後 書き込み SQLite RESTful APIでサーバーを経由してFirestoreに書き込む 読み込み
SQLite clientから直接Firestoreを読み込む APIドキュメント: Swagger
v2 構成での問題点 ログイン前のSQLiteのDB構成を、そのままFirestoreのデータ構成にしていたのでFirestoreのメリット が活かなかった clientから直接Firestoreを読み込む処理を宣⾔する関係でアプリのコードが多くなり、複雑になった RESTful APIやFirestoreに関するTypeをフロントエンド、バックエンドのプロジェクト毎にTypeを管理 するのが⾟い Firestoreのsecurity rulesが複雑
Swaggerの更新を忘れて実装と乖離する
Firestore の特徴 データモデルは階層型データ構造 RDBのJOINは存在しない where-inやarray-contains-anyは、双⽅10件までしか取得できない制約がある •結論: 上記の性質からRDBと同じ設計思想で進めると、どこかしらで躓く という事でv3で、どう改善したかという話が次になります。
v3 の構成 項⽬ ログイン前 ログイン後 書き込み SQLite GraphQLを経由してFirestoreに書き込む 読み込み SQLite
GraphQLを経由してFirestoreを読み込む
v3 での改善点① ログイン前のSQLite のDB 構成を、そのままFirestore のデータ構成にしていたので Firestore のメリットが活かなかった データ構成をFirestoreの活かせる作りに変更した client
から直接Firestore を読み込む処理を宣⾔する関係でアプリのコードが多くなり、複雑になった Firestoreのアクセスコードが全てbackend側になったのでアプリ側の実装がシンプルになった
v3 での改善点② RESTful API やFirestore に関するType をフロントエンド、バックエンドのプロジェクト毎にType を管理するのが⾟い graphql-codegenを使⽤することでtypeは全て⾃動⽣成できるようになった Firestore
のsecurity rules が複雑 Firestoreのアクセスが全てサーバー側になったため考慮する必要がなくなった Swagger の更新を忘れて実装と乖離する APIドキュメントは、GraphQLから⾃動⽣成できるので実装と乖離はしなくなった
逆に v3 の構成で失ったもの Firestoreのアクセス全てサーバー側で実装したためリアルタイムアップデートの機能は失った ※ ⼀応GraphQLのSubscriptionsを実装すれば、リアルタイムアップデートの実装も可能だがコストと⾒合 わないため今回は⾒送っています
データ構成の⽐較 •v2でのFirestoreのデータ構成 ├──users/:id ├──calendars/:id ├──items/:id ├──itemDetails/:id └──expoPushTokens/:id •v3でのFirestoreのデータ構成 version/1 └──
users/:id ├── expoPushTokens/:id └── calendars/:date └──items/:id └──itemDetails/:id
v3 での Firestore のデータ構成のメリット user_idのみでユーザーの全情報を取得できる user_idと⽇付でcalendarの情報が取得できる calendarのDocumentIDが⽇付なので、必ずユニークの構造になる where-inやarray-contains-anyを使わずに書くデータを取得できる version/1 └──
users/:id ├── expoPushTokens/:id └── calendars/:date └──items/:id └──itemDetails/:id
各画⾯とデータの読み込み
カレンダー画⾯ カレンダーに表⽰する分のデータが必要なので以下のような クエリで取得 f.Collection("version/1/users/1/calendars").Where("date", ">=", "2020-12-01T00:00:00") .Where("date", "<=", "2020-12-31T31:59:59")
ライフログ画⾯ 1⽇に表⽰する分のデータが必要なので以下のようなクエリで 取得 f.Collection("version/1/users/1/calendars/2020-12-01")
ライフログ詳細画⾯ ここではidだけデータを取得したいのでCollectionGroupを 使⽤して取得 f.CollectionGroup("itemDetails").Where("id", "==", "efjhij")
CollectionGroup とは? Firestoreのデータ構造上、親階層のDocuementIDの情報を持っていない限り、 ⼦階層へのアクセスはできませんが、CollectionGroupを使⽤することで、横断的にアクセ スすることができます。 •通常のitemDetailのアクセス f.Collection("version/1/users/1/calendars/2020-12-01/items/abcded/itemDetails") .Where("id", "==", "efjhij")
•CollectionGroupを使⽤したアクセス f.CollectionGroup("itemDetails").Where("id", "==", "efjhij")
CollectionGroup の注意点 DocuementIDが重複する可能性がある コレクションIDが⼀致すればデータ構造関係無しにデータを取得してしまう 別階層に同名のコレクションIDが存在する場合でもデータは取得できる whereは単⼀かつ範囲系の条件は指定できない
データ構造変更によるマイグレーション もちろん、構造の変更するには実際のデータマイグレーションが必要なので、以下でマイグ レーションスクリプトを作成 https://github.com/wheatandcat/PeperomiaTool/tree/master/FirestoreMigration 詳しくは以下、参照 記事: Firestoreを新設計にマイグレーションする
バックエンドの実装
GraphQL の実装 GraphQLはgqlgenを使⽤して実装 •gqlgenとは https://github.com/99designs/gqlgen GraphQLのスキーマファイルからGraphQLのコードを⾃動⽣成機能を持つフレームワークで す。
•gqlgenの⾃動⽣成 こんな感じでスキーマファイルからコードが⾃動⽣成されます。
作成した GraphQL ⼀覧 https://github.com/wheatandcat/PeperomiaBackend/blob/master/schema.md
アプリの実装
アプリの GraphQL の繋ぎ込み GraphQLの繋ぎ込みはgraphql-codegenを使⽤して実装 •graphql-codegenとは? graphql-codegenを使⽤することでGraphQLのスキーマファイルとgqlファイルから、 TypesやCustom Hooksでのアクセスのコードを⾃動⽣成することができます。
graphql-codegen で Query( 読み込み ) の⾃動⽣成 gqlファイルを元に、以下のコードが⾃動⽣成されます。
⾃動⽣成されたコードを使⽤してデータを取得する ⾃動⽣成されたuseCalendarQueryに引数を渡すことでgraphqlのデータを取得できます。 import React ,{ memo } from 'react'; import
{ useCalendarQuery } from 'queries/api/index'; const Connected: React.FC<Props> = memo((props) => { const { data, loading, error } = useCalendarQuery({ variables: { date: props.date, }, fetchPolicy: 'network-only', });
graphql-codegen で Mutation( 書き込み ) の⾃動⽣成 gqlファイルを元に、以下のコードが⾃動⽣成されます。
⾃動⽣成されたコードを使⽤してデータを更新する ⾃動⽣成されたuseCreateCalendarを使⽤してデータを更新する。 import React ,{ memo, useCallback } from 'react';
import { useCreateCalendarMutation, NewItem } from 'queries/api/index'; const Connected: React.FC<Props> = memo((props) => { const [ createCalendarMutation ] = useCreateCalendarMutation({ onCompleted({ createCalendar }) { props.navigation.navigate('Calendar', { date: dayjs(createCalendar.date).format('YYYY-MM-DDT00:00:00'), }); }, onError(err) { Alert.alert(' 作成に失敗しました', err.message); }, }); const onSave = useCallback( (item: NewItem) => { const variables = { calendar: { date: props.date, item, }, }; createCalendarMutation({ variables }); }, [createCalendarMutation, props.date] );
SQLite と GraphQL の切り替え ログイン前はSQLite、ログイン後はGraphQL(Firestore) でデータ管理しているので、 その部分をCustom Hooksを使⽤して実装 import {
useAuth } from 'containers/Auth'; import { CalendarQueryHookResult, useCalendarQuery, CalendarQueryVariables } from 'queries/api/index'; import { WatchQueryFetchPolicy } from '@apollo/client'; import useCalendarDB from 'hooks/db/useCalendarDB'; import { isLogin } from 'lib/auth'; const useCalendar = (props: Props) => { const { uid } = useAuth(); let useHooks: UseHooks; if (isLogin(uid)) { // ログイン時は⾃動⽣成されたHooks を使⽤ useHooks = useCalendarQuery; } else { // ログイン前は上記と同じインターフェースで⾃作したSQLite アクセス⽤のCustom Hooks を使⽤ useHooks = useCalendarDB } return useHooks(props); }; export default useCalendar;
データ設計に合わせてデザインを変更
データ設計からの画⾯設計 今回のデータ設計の変更によりライフログは全てカレンダーに紐づくようになったので、 諸々画⾯設計の⾒直しをしました。 •v3でのデータ設計 version/1 └── users/:id ├── expoPushTokens/:id └──
calendars/:date └──items/:id └──itemDetails/:id
ホーム画⾯ ホームは登録⼀覧画⾯からカレンダー画⾯に変更 ボトムタブもカレンダー、今⽇の予定、設定に変更 データ構造をカレンダーベースに変更した流れで画⾯設計合わせ て変更
ライフログ画⾯ 縦軸の⼀覧表⽰からマルチデバイスを意識したデザインに変更 ライフログアプリに時間の概念は不要と判断したため削除
ライフログ詳細画⾯ ほぼ変更なし 時間の表⽰のみ削除
最後に
諸々設計し直しての感想 Firestoreは設計が重要 gqlgenとgraphql-codegenは組み合わせでbackendとfrontendの乖離をカバーできる gqlgenはモック実装も、ほぼノーコストで⾏えるのでチーム開発でも優良 フロントエンド界隈でもGraphQL周りの便利ツールがでてきたので積極的に試していき たい https://github.com/dotansimha/graphql-eslint https://github.com/apollographql/eslint-plugin-graphql
Thank You