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
77
作成中のFlutterアプリの中間発表
wheatandcat
0
62
最近読んだ技術書を簡単紹介
wheatandcat
0
83
ユニバーサルリンク/アプリリンクを使ってQRコードでゲストログインできるようにする
wheatandcat
0
340
Firebase App Checkを実装したので紹介
wheatandcat
0
210
PlanetScaleの無料プランがなくなるので、NeonとTiDBを試してみた
wheatandcat
0
350
Flutter HooksとRiverpodの解説
wheatandcat
0
480
T3 Stack(応用編: Next Auth & SSRの実装紹介)
wheatandcat
1
370
App Routerの紹介
wheatandcat
0
120
Other Decks in Programming
See All in Programming
ELYZA_Findy AI Engineering Summit登壇資料_AIコーディング時代に「ちゃんと」やること_toB LLMプロダクト開発舞台裏_20251216
elyza
2
1.4k
AIフル活用時代だからこそ学んでおきたい働き方の心得
shinoyu
0
120
20260127_試行錯誤の結晶を1冊に。著者が解説 先輩データサイエンティストからの指南書 / author's_commentary_ds_instructions_guide
nash_efp
0
830
Rust 製のコードエディタ “Zed” を使ってみた
nearme_tech
PRO
0
110
0→1 フロントエンド開発 Tips🚀 #レバテックMeetup
bengo4com
0
530
Patterns of Patterns
denyspoltorak
0
1.3k
Apache Iceberg V3 and migration to V3
tomtanaka
0
130
Denoのセキュリティに関する仕組みの紹介 (toranoana.deno #23)
uki00a
0
280
メルカリのリーダビリティチームが取り組む、AI時代のスケーラブルな品質文化
cloverrose
2
510
Grafana:建立系統全知視角的捷徑
blueswen
0
320
.NET Conf 2025 の興味のあるセッ ションを復習した / dotnet conf 2025 quick recap for backend engineer
tomohisa
0
130
コマンドとリード間の連携に対する脅威分析フレームワーク
pandayumi
1
440
Featured
See All Featured
Navigating the moral maze — ethical principles for Al-driven product design
skipperchong
2
240
Building Flexible Design Systems
yeseniaperezcruz
330
40k
Designing Powerful Visuals for Engaging Learning
tmiket
0
210
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
150
The Curse of the Amulet
leimatthew05
1
8k
Getting science done with accelerated Python computing platforms
jacobtomlinson
1
110
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
0
420
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
280
We Have a Design System, Now What?
morganepeng
54
8k
Into the Great Unknown - MozCon
thekraken
40
2.2k
Navigating Algorithm Shifts & AI Overviews - #SMXNext
aleyda
0
1.1k
Music & Morning Musume
bryan
47
7.1k
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