tag:crieit.net,2005:https://crieit.net/tags/SSR/feed 「SSR」の記事 - Crieit Crieitでタグ「SSR」に投稿された最近の記事 2019-05-12T08:36:22+09:00 https://crieit.net/tags/SSR/feed tag:crieit.net,2005:PublicArticle/14665 2018-12-16T23:27:12+09:00 2019-05-12T08:36:22+09:00 https://crieit.net/posts/Nuxt-js-Firestore-SSR Nuxt.js+Firestoreの場合に安全にSSRする方法 <p>Nuxt.jsをサーバーで動かしてFirestoreを使い、且つサーバーサイドレンダリングしたい時には色々考えることが出てきます。懸念点と対応した方がよさそうな点をまとめてみます。</p> <h2 id="SSRしない場合との違い"><a href="#SSR%E3%81%97%E3%81%AA%E3%81%84%E5%A0%B4%E5%90%88%E3%81%A8%E3%81%AE%E9%81%95%E3%81%84">SSRしない場合との違い</a></h2> <p>SSRせずクライアント上でFirestoreからデータを取る場合ですが、匿名認証にしろ、SNSアカウントの認証にしろ、認証しているかどうかでセキュリティルールによりデータを守りやすく、他者によるFirebaseプロジェクトの設定を盗用しての勝手な操作を防いだりすることができます。</p> <p>しかし、asyncData内によるSSRとなると、そのクライアント側の認証情報を利用することはできません。そうなるとデータを守れないどころか、そもそもセキュリティルールが設定されているとデータにアクセスすることすらできません。こういう時にどうすれば良いかを考えます。</p> <h2 id="どのように解決するか"><a href="#%E3%81%A9%E3%81%AE%E3%82%88%E3%81%86%E3%81%AB%E8%A7%A3%E6%B1%BA%E3%81%99%E3%82%8B%E3%81%8B">どのように解決するか</a></h2> <h3 id="Cloud Functionsの利用"><a href="#Cloud+Functions%E3%81%AE%E5%88%A9%E7%94%A8">Cloud Functionsの利用</a></h3> <p>今回はCloud Functionsを利用する方法を考えました。具体的にどうするかというと、asyncData内ではfunctionsからデータを取るだけにします。例えば下記のような感じです。</p> <pre><code class="javascript"> async asyncData({ params }) { const response = await axios.get(functionUrl) return response.data } </code></pre> <p>呼び出し方はどうであれ、通信が発生するので上記のようにリクエストは1回にした方が良いでしょう。functions側でそのページ専用のアクションを作ってデータを返します。</p> <pre><code class="javascript">export const showUser = functions.https.onRequest((req, res) => { return cors(req, res, async () => { const user = await User.getUserByUsername(req.query.id) res.json({ user, posts: await Post.getPosts(user.uid, 5) }) }) }) </code></pre> <h3 id="データの取得方法"><a href="#%E3%83%87%E3%83%BC%E3%82%BF%E3%81%AE%E5%8F%96%E5%BE%97%E6%96%B9%E6%B3%95">データの取得方法</a></h3> <p>しかし、これでは先程のasyncDataと同様、セキュリティルールの関係でデータが取れないのでは? と思われるかもしれません。ただ、Firebaseにはサーバー用のFirebase Admin SDKというものがあります。これはセキュリティルールに関係なくデータを扱うことができるため、これを利用します。具体的には下記のような形です。</p> <pre><code class="typescript">import * as admin from 'firebase-admin' const firestore = admin.firestore() const usersCollection = firestore.collection('users') export async function getUser(uid: string) { const querySnapshot = await usersCollection .where('uid', '==', uid) .get() .catch(error => { console.error(error) return null }) if (!querySnapshot || querySnapshot.size == 0) { return null } const user = querySnapshot.docs[0].data() user.id = querySnapshot.docs[0].id return user } </code></pre> <p>クライアント側とは微妙にcollectionの取り方が違うだけで、基本的にはほぼ同じです。このようにしてサーバーサイドレンダリングのためのデータ取得処理を行うことができます。</p> <h2 id="セキュリティルールを無視で良いのか?"><a href="#%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%83%AB%E3%83%BC%E3%83%AB%E3%82%92%E7%84%A1%E8%A6%96%E3%81%A7%E8%89%AF%E3%81%84%E3%81%AE%E3%81%8B%EF%BC%9F">セキュリティルールを無視で良いのか?</a></h2> <p>今回の例のfunctions側でのデータ取得処理に関しては、ルールを無視で問題ありません。</p> <p>というのも、そもそもSSRの目的としては、クローラに認識してもらうためのSEO対策になるかと思います。つまり、ユーザー認証が必要なデータを取る必要は一切無いということになります。あくまでも誰もが閲覧できるパブリックなデータを取るだけです。そのため基本的には認証に関するセキュリティルールをこの場で考慮する必要はありません。ただ、corsはちゃんと挟んでおきましょう。</p> <h2 id="まとめ"><a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></h2> <p>やってみて思いましたが、これって普通のサーバーを使った開発と同じですよね!? なんとなく手軽さが失われた感じはありますが、それでもまあサーバーを管理する必要が無いのは大きなメリットではあることに変わりはないでしょう。</p> <p>他にも色々と方法はあるかもしれませんが、とにかくどの様な方法にしろ、どのようにデータを守るかはしっかり考えて構築が必要となります。</p> <p>以前認証とセキュリティルールについて書いた考察もありますので、もし気が向いたらそちらもぜひ御覧ください。</p> <p><a href="https://crieit.net/posts/Firebase">Firebaseの匿名認証はなんのためにあるのか - セキュリティ編</a></p> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/14518 2018-08-26T23:57:05+09:00 2018-10-29T18:03:13+09:00 https://crieit.net/posts/Nuxt-js-SSR Nuxt.jsでSSRかブラウザ上の処理かを判定する方法の一つ <p>Nuxt.jsで開発する場合、書いた処理がサーバーサイドレンダリング(SSR)時と、ブラウザ上での処理時の両方で実行される場合がある。それを判定したい時の方法。</p> <h2 id="正規の方法"><a href="#%E6%AD%A3%E8%A6%8F%E3%81%AE%E6%96%B9%E6%B3%95">正規の方法</a></h2> <p>正規の方法として、公式にも書かれているやり方。</p> <p><a target="_blank" rel="nofollow noopener" href="https://nuxtjs.org/faq/window-document-undefined/">window or document undefined?</a></p> <p>こんな感じで判定できる。</p> <pre><code class="javascript">if (process.browser) { require('external_library') } </code></pre> <h2 id="TypeScriptの場合"><a href="#TypeScript%E3%81%AE%E5%A0%B4%E5%90%88">TypeScriptの場合</a></h2> <p>ただし、TypeScriptの場合はprocessの型にbrowserというプロパティが無いため、エラーになってしまう。そのため色々あれこれ型をうまい事設定する方法も可能かもしれないが、とりあえず古式ゆかしい方法でも判定することができる。</p> <pre><code class="javascript"> if (typeof window !== "undefined") { </code></pre> だら@Crieit開発者 tag:crieit.net,2005:PublicArticle/14504 2018-08-11T16:55:46+09:00 2019-01-22T20:16:45+09:00 https://crieit.net/posts/Nuxt-document-is-not-defined Nuxtでdocument is not definedエラー <p>Nuxt.jsにて、npmで提供されているVueコンポーネントを利用しようとした時に、<code>ReferenceError: document is not defined</code>というエラーが出ることがあるのでそれの解決方法。</p> <h2 id="とりあえず解決方法は?"><a href="#%E3%81%A8%E3%82%8A%E3%81%82%E3%81%88%E3%81%9A%E8%A7%A3%E6%B1%BA%E6%96%B9%E6%B3%95%E3%81%AF%EF%BC%9F">とりあえず解決方法は?</a></h2> <p>例えば僕の愛用している<code>JohMun/vue-tags-input</code>でも出るのだが、その場合下記のようにテンプレートを書く。</p> <pre><code class="html"><br /> <no-ssr> <vue-tags-input v-model="tag" :tags="tags" @tags-changed="newTags => tags = newTags" /> </no-ssr> </code></pre> <p>プラグイン追加で出る場合は、nuxt.config.jsの該当プラグインで<code>ssr: false</code>にすれば解決する場合もある(プラグインの説明に書いてあったりする)</p> <h2 id="なぜ発生するのか?"><a href="#%E3%81%AA%E3%81%9C%E7%99%BA%E7%94%9F%E3%81%99%E3%82%8B%E3%81%AE%E3%81%8B%EF%BC%9F">なぜ発生するのか?</a></h2> <p>エラーと対応内容を見ればだいたい分かると思うが、そもそもSSR(サーバーサイドレンダリング)を考慮していないVueコンポーネントも存在し、そういったライブラリは今回のようにブラウザ上だけで存在している、<code>document</code>を参照している場合がある。</p> <p>サーバーサイドレンダリングの場合はブラウザではないため当然<code>document</code>は存在しないため<code>undefined</code>となりエラーになる。そのため、<code><no-ssr></code>タグで囲ってあげることで、ブラウザ上では表示するが、サーバーサイドレンダリングのタイミングでは動作させないようにすることでエラーを防ぐことができる、という仕組み。</p> <p>そのため、当然Nuxt.jsに限らずサーバーサイドレンダリングしている場合は発生する場合がある。</p> だら@Crieit開発者