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
130
0
Share
React Native + Firestoreで運用しているアプリをRESTful APIからGraphQLに移行した話
ペペロミアでRESTful APIからGraphQLに移行した話をまとめました。
■ ペペロミア
https://peperomia.app/
Yohei Iino
December 15, 2020
More Decks by Yohei Iino
See All by Yohei Iino
1年半放置したExpo製アプリを最新化してみた
wheatandcat
0
87
作成中のFlutterアプリの中間発表
wheatandcat
0
74
最近読んだ技術書を簡単紹介
wheatandcat
0
95
ユニバーサルリンク/アプリリンクを使ってQRコードでゲストログインできるようにする
wheatandcat
0
360
Firebase App Checkを実装したので紹介
wheatandcat
0
250
PlanetScaleの無料プランがなくなるので、NeonとTiDBを試してみた
wheatandcat
0
380
Flutter HooksとRiverpodの解説
wheatandcat
0
530
T3 Stack(応用編: Next Auth & SSRの実装紹介)
wheatandcat
1
380
App Routerの紹介
wheatandcat
0
130
Other Decks in Programming
See All in Programming
一度始めたらやめられない開発効率向上術 / Findy あなたのdotfilesを教えて!
k0kubun
3
2.7k
GC言語のWasm化とComponent Modelサポートの実践と課題 - Scalaの場合
tanishiking
0
140
OTP を自動で入力する裏技
megabitsenmzq
0
130
GoのDB アクセスにおける 「型安全」と「柔軟性」の両立 - Bob という選択肢
tak848
0
300
仕様漏れ実装漏れをなくすトレーサビリティAI基盤のご紹介
orgachem
PRO
8
4.2k
The free-lunch guide to idea circularity
hollycummins
0
400
それはエンジニアリングの糧である:AI開発のためにAIのOSSを開発する現場より / It serves as fuel for engineering: insights from the field of developing open-source AI for AI development.
nrslib
1
820
AIと共にエンジニアとPMの “二刀流”を実現する
naruogram
0
120
事業会社でのセキュリティ長期インターンについて
masachikaura
0
210
Laravel Nightwatchの裏側 - Laravel公式Observabilityツールを支える設計と実装
avosalmon
1
300
20260313 - Grafana & Friends Taipei #1 - Kubernetes v1.36 的開發雜記:那些困在 Alpha 加護病房太久的 Metrics
tico88612
0
250
CS教育のDX AIによる育成の効率化
niftycorp
PRO
0
180
Featured
See All Featured
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.7k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
160
Winning Ecommerce Organic Search in an AI Era - #searchnstuff2025
aleyda
1
1.9k
B2B Lead Gen: Tactics, Traps & Triumph
marketingsoph
0
95
Efficient Content Optimization with Google Search Console & Apps Script
katarinadahlin
PRO
1
460
The Invisible Side of Design
smashingmag
302
51k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
Build The Right Thing And Hit Your Dates
maggiecrowley
39
3.1k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
287
14k
The browser strikes back
jonoalderson
0
870
From π to Pie charts
rasagy
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