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
100
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
PlanetScaleの無料プランがなくなるので、NeonとTiDBを試してみた
wheatandcat
0
26
Flutter HooksとRiverpodの解説
wheatandcat
0
38
T3 Stack(応用編: Next Auth & SSRの実装紹介)
wheatandcat
1
99
App Routerの紹介
wheatandcat
0
41
Flutter × GraphQLでアプリを作ってみる
wheatandcat
0
150
T3 Stack + Supabaseでアプリを作ってみる
wheatandcat
0
130
Supabaseの紹介
wheatandcat
0
130
tRPCの紹介
wheatandcat
0
110
Plasmoの紹介
wheatandcat
0
52
Other Decks in Programming
See All in Programming
AHC035解説
terryu16
0
720
DDDを志して3年経ったら「DDDの皮を被ったクリーンアーキテクチャ」になった話【デブサミ2024夏】
texmeijin
1
620
ピグパーティにおけるMongoDB CommunityバージョンからAtlasへの移行事例
10969hotaka
0
130
SDCon2024: Enabling DevOps and Team Topologies thru architecture: architecting for fast flow
cer
PRO
0
780
feature環境をGitHub ActionsとCloudFormationでいい感じに管理する
nealle
2
310
ぼっちを避けて楽しむためのアノテコノテ / Various Tips and Tricks to Avoid Loneliness and Have Fun
nrslib
3
1.7k
継続的な活動で築く地方エンジニアの道
myamashii
2
360
実用的かつリーズナブルな 「Azure × Gemini × LINE」~キャラクターBot 実装ライブデモ~
tomodo_ysys
1
170
Rust.Nagoya #1
codemountains
0
170
Harnessing Large Language Models for Training-free Video Anomaly Detection
tereka114
1
1.3k
「2024年版 Kotlin サーバーサイドプログラミング実践開発」の補講 〜O/Rマッパー編〜
n_takehata
2
260
Xcode 16のPreviewModifierと@Previewableを活用した効率的なプレビュー方法の考察
ojun9
2
160
Featured
See All Featured
Dealing with People You Can't Stand - Big Design 2015
cassininazir
360
22k
Optimizing for Happiness
mojombo
373
69k
No one is an island. Learnings from fostering a developers community.
thoeni
17
2.8k
Learning to Love Humans: Emotional Interface Design
aarron
269
39k
Bootstrapping a Software Product
garrettdimon
PRO
304
110k
Being A Developer After 40
akosma
72
580k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
12
3.8k
Fireside Chat
paigeccino
25
2.8k
YesSQL, Process and Tooling at Scale
rocio
166
14k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
26
1.8k
It's Worth the Effort
3n
181
27k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
353
29k
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