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...
Search
Yohei Iino
December 15, 2020
Programming
0
120
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
1年半放置したExpo製アプリを最新化してみた
wheatandcat
0
73
作成中のFlutterアプリの中間発表
wheatandcat
0
59
最近読んだ技術書を簡単紹介
wheatandcat
0
79
ユニバーサルリンク/アプリリンクを使ってQRコードでゲストログインできるようにする
wheatandcat
0
330
Firebase App Checkを実装したので紹介
wheatandcat
0
200
PlanetScaleの無料プランがなくなるので、NeonとTiDBを試してみた
wheatandcat
0
340
Flutter HooksとRiverpodの解説
wheatandcat
0
450
T3 Stack(応用編: Next Auth & SSRの実装紹介)
wheatandcat
1
360
App Routerの紹介
wheatandcat
0
120
Other Decks in Programming
See All in Programming
2年のAppleウォレットパス開発の振り返り
muno92
PRO
0
120
フルサイクルエンジニアリングをAI Agentで全自動化したい 〜構想と現在地〜
kamina_zzz
0
310
Developing static sites with Ruby
okuramasafumi
0
330
Rubyで鍛える仕組み化プロヂュース力
muryoimpl
0
200
AtCoder Conference 2025「LLM時代のAHC」
imjk
2
600
ゆくKotlin くるRust
exoego
1
160
AIコーディングエージェント(NotebookLM)
kondai24
0
240
実は歴史的なアップデートだと思う AWS Interconnect - multicloud
maroon1st
0
270
Graviton と Nitro と私
maroon1st
0
140
Flutter On-device AI로 완성하는 오프라인 앱, 박제창 @DevFest INCHEON 2025
itsmedreamwalker
1
160
Context is King? 〜Verifiability時代とコンテキスト設計 / Beyond "Context is King"
rkaga
10
1.4k
Go コードベースの構成と AI コンテキスト定義
andpad
0
140
Featured
See All Featured
The Pragmatic Product Professional
lauravandoore
37
7.1k
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
1
580
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
0
78
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.7k
SEOcharity - Dark patterns in SEO and UX: How to avoid them and build a more ethical web
sarafernandez
0
89
Darren the Foodie - Storyboard
khoart
PRO
0
2k
Being A Developer After 40
akosma
91
590k
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
0
100
Imperfection Machines: The Place of Print at Facebook
scottboms
269
13k
WENDY [Excerpt]
tessaabrams
8
35k
How to Get Subject Matter Experts Bought In and Actively Contributing to SEO & PR Initiatives.
livdayseo
0
31
Un-Boring Meetings
codingconduct
0
160
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