背景
サーバーサイドの人がフロントエンドを結構こだわったプロジェクトをやるときに、混乱することが多そうなので、(社内向けに) 自分がいろいろ作ったなかで思ったベストプラクティス(?)を共有する
用語
- SPA: Single Page Application. 単一のページ (index.html) で構成されるウェブアプリケーション
- SSR: Server-Side Rendering. SPA を事前に HTML として描画するやつ
- JAMstack: ウェブアプリケーション配信の構成で、JAM は JavaScript+API+Markup の略
- Vue.js: UI を作るための JavaScript のフレームワーク
- Nuxt.js: Vue をベースにしたフレームワークみたいなもので、SPA、SSR 、JAMstack にも対応している
もう少しちゃんとした説明は、各キーワードでググって下さい。
サンプルリポジトリ
https://github.com/yamitzky/nuxt-api-example
前提
プロジェクト全体の前提としては、
- 「SPA と API」という構成にする
- フロントエンドは Nuxt などのレールに乗せる
- (ほぼ)同じオリジンとして扱う
そもそもフロントエンドは独立したコードベース (≒ SPA) にしたが良いのか?
例えば管理画面を作るときなど、例えば Django や Rails などで SPA ではない構成を取ることが多いと思う。ケースバイケースだが、フロントエンドを独立したコードベースの SPA にすると下記のようなメリットがある。
- フロントエンドのコードが複雑になったとき、破綻しづらい
- コンポーネント指向な設計になる
- Flux なり Vuex なり、状態管理の設計が強制される
- フロントエンドのエコシステムに乗っかれる
- ホットリロード
- エディタサポート
- JAMStackのメリットが得られる
- ようするに餅は餅屋
ただし、 qrunch.io は Rails と turbolinks ではあるが、SPA のようなサクサクしたユーザー体験を提供できている、という例もある。ので、結局好みでは。
ディレクトリ構成
ディレクトリは次のような構成にする。api 以下は Python とかの世界。frontend 以下は Nuxt とかの世界。「frontendプロジェクト」と「apiプロジェクト」の2つのリポジトリがあるモノレポみたいなもの。
こうすると、Docker のイメージが効率良く作りやすくなる、それぞれのホットリロードが正しく効くとかのメリットがある。
project ├── api └── frontend
配信構成
nginx、api の2つのコンテナから成り立たせる。
nginx (example.com) -> /api/* -----> api のコンテナ -> それ以外 --> 静的ファイルとしてフロントエンドを配信
あるいは、静的ファイルは CDN にデプロイする
example.com ------> CDN 上で配信 api.example.com --> api のコンテナ
JAMstack としては、静的ファイルは CDN に乗せるのが良さそうなんだけど、SSR ができないというデメリットもあるので、どちらでも良さそう。(パフォーマンス要件などに応じて適当に考える)
ここで大事なのは、*Django などの API 側フレームワークは一切の静的ファイルを配信しないこと。JSONを吐き出すのが責務
開発時の配信構成
docker-compose などで、 frontend のコンテナと api のコンテナがある想定
frontend (localhost:3000) -> /api/* -----> http://api:8080/* をプロキシ -> それ以外 --> frontend のプロジェクトとして普通に配信 あるいは、docker-compose を使いたくないなら frontend (localhost:3000) -> /api/* -----> http://localhost:8080/* をプロキシ -> それ以外 --> frontend のプロジェクトとして普通に配信
frontend のプロジェクトには、たいてい、開発用にプロキシする機能がついている(nuxt, vue-cli)。
デバッグするときに見るのは、 localhost:3000 だけ。そうすると CORS とか Cookie の問題が解消するので、ハッピーです。
Nuxt 使おうぜ話
概ね、 Nuxt のレール乗っかっておくと非常に楽。
- 設計が強制される
- SPA のために必要な機能が揃ってる
- store のモジュール化なども自動で対応してくれて便利
~/hoge
でアクセスできたりして便利- SSR も対応
- ビルドパイプラインのメンテが簡易
もちろん Nuxt 以外のレールでももちろん良いが、 Vue は easy と言われることもあり、学習コストが低めで良いのではないか、というスタンス。
認証
Nuxt の認証はだいたい3パターンぐらいあって、
- セッションを使った認証 (例1、例2)
- JWTなどのトークンでの認証 (例)
- @nuxtjs/auth プラグイン ※使ったことないけど楽っぽい予感
セッションや JWT を使うのは、Nuxt じゃない技術選定をしても、だいたいそれらをどう保持するかという話になりそう。localstorage かセッション、Cookie に保持する。
TODO
- PWA、キャッシュ
おまけ:環境変数とか
- yarn
- pipenv
- .env
- direnv
を使っている。
サンプルプロジェクトだと、あえて .env と .envrc をリポジトリーに追加している。
.env と direnv の使い分けは、
- .env: 環境変数を定義する。 JetBrains 系、VSCode などのエディターが .env をサポートしているため
- direnv: .env に定義された環境変数や、
pipenv --venv
で得られる環境変数をロードする。.envrc には、環境変数の定義自体はしない
# .envrc source `pipenv --venv`/bin/activate export `cat .env`