Django+SPA(Nuxt.js)の環境をHerokuにデプロイしてみた
バックエンドはDjangoでフロントエンドはNuxt.jsのようなSPAで環境をheroku上で構築したいのですが、課題がいくつか出てきました。
- Herokuデプロイ時にNuxt.jsのビルドはどうするのか(gitレポジトリにビルド生成物を置きたくない!)
- Nuxt.jsで生成された静的ファイルをDjangoからどうハンドリングするのか(nginxには頼れない!)
- Nuxt.jsのページルーティングをDjangoからどうハンドリングするのか
一つ一つ解決していきましょう。
- Django + SPAの環境をHerokuにデプロイするときの課題
- まずはプロジェクトの作成(Django & Nuxt.js)
- Nuxt.jsで生成されるアセットをDjangoで静的ファイルとして扱えるようにする
- DjangoでSPAのページルーティングに対応させる
- Heroku対応
Django + SPAの環境をHerokuにデプロイするときの課題
バックエンドはDjangoでフロントエンドはNuxt.jsのようなSPAで環境を構築したいケースはよくあります。
Nuxt.jsサーバを立てる場合は、DjangoとNuxt.jsそれぞれのレポジトリを作って、別々の2つのHeroku環境にデプロイすればいいです。
今回はNuxt.jsを使いますが、Nuxt.jsのサーバは立てずに、静的なフロントエンド環境を前提とします。そうするとデプロイ時にいくつかの課題点が出てきます。
ビルドの課題
HerokuではBuildpacksというものを使って、デプロイ時にビルドしてくれます。
Buildpacksは標準で多くの言語が用意されています。
devcenter.heroku.com
Herokuが便利なのはレポジトリのルートディレクトリを見て自動的に言語を判定してくれます。言語だけでなくフレームワークまで判定してくれるのです。
例えば、nodejsだったらyarnを使うのかnpmを使うのか、pythonだったらpipかpipenvか、rubyだったらrailsのバージョンやその他のrackアプリケーションか。
自動判定のおかげで何もせずともいい感じにビルドが入ってデプロイしてくれます。
今回のケースのように2つのbuildpack、python(Django)とnodejs(Nuxt.js)を走らせたいときは少し工夫が必要です。
Djangoでの静的ファイルについての課題
Djangoは静的ファイルを扱うのに少し癖があります。
基本的には /static
というパス以下を静的ファイルとして扱います。
Nuxt.jsや多くのSPAフレームワークではindex.htmlを生成してくれますが、その中で生成されたjavascript等のアセットのパスは決まっています。Nuxt.jsの場合は/_nuxt/xxxxxx.js
のようなパスになり、それがindex.htmlの中に直書きされます。
RailsやLaravelだったらpublicディレクトリに放り込んでおけば、そのままのパスで静的ファイルとして認識してくれるのですけどね。Djangoだと細工が必要です。
まずはプロジェクトの作成(Django & Nuxt.js)
DjangoもNuxt.jsもコマンドをいくつかたたくだけで、プロジェクトができてしまいます。
まずはそれぞれのプロジェクトを作っておきます。
Djangoプロジェクトの作成
herokuではDjangoのサーバを動かしますので、まずはプロジェクトのルートディレクトリにDjangoプロジェクトを作ります。
まずdjangoやその他のライブラリをインストールしましょう。
最低限必要なのは4つです。
python仮想環境/パッケージ管理ツールとしてpipenvを使っていきます。
$ pipenv install django gunicorn whitenoise django-heroku
とりあえずhelloというプロジェクトを作ってみます。
$ pipenv shell $ django-admin startproject hello .
Djangoを起動すると無事にブラウザでアクセスできるようになりました。
http://127.0.0.1:8000/
$ python manage.py runserver
Djangoのプロジェクトはここでおいておきます。

- 作者:横瀬 明仁
- 発売日: 2018/08/26
- メディア: オンデマンド (ペーパーバック)
Nuxt.jsプロジェクトの作成
ルートディレクトリにそのままつくると少し煩雑になるので、client
ディレクトリをつくってその下にNuxt.jsプロジェクトを作りましょう。
$ mkdir client $ cd client $ npx create-nuxt-app .
いくつかの質問にポチポチ答えるとあっという間にプロジェクトができます。
今回は以下のような感じで答えました。
create-nuxt-app v3.4.0 ✨ Generating Nuxt.js project in . ? Project name: client ? Programming language: TypeScript ? Package manager: Yarn ? UI framework: None ? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert se lection) ? Linting tools: ESLint ? Testing framework: None ? Rendering mode: Single Page App ? Deployment target: Static (Static/JAMStack hosting) ? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection) ? Continuous integration: None ? Version control system: None
パッケージマネージャはyarnを選びましたが、これは後で使います。
Git(Version control system)は上の階層(プロジェクルート)でつくるので、ここではいらないですね。
Nuxt.jsサーバを立ち上げてみましょう。
http://127.0.0.1:3000/
$ yarn dev
Nuxt.jsで生成されるアセットをDjangoで静的ファイルとして扱えるようにする
Nuxt.jsの設定
Djangoでは基本的には/static
パス以下を静的ファイルとして扱います。
Nuxt.jsから静的ファイルを生成してみましょう。
$ cd client $ yarn generate
標準ではclient/dist
ディレクトリ以下にindex.htmlやその他の静的ファイルが生成されます。
javasscript関連はclient/dist/_nuxt
ディレクトリです。index.htmlの中をのぞいてみても、当然のような文字列が書かれています。
Nuxt.jsではアセットのパスを変更することができます。nuxt.config.js
ファイルの build.publicPath
を次のようにしてみましょう。
build: { publicPath: '/static/_nuxt/' }
この状態で再度generateしてみると、javasscript関連はclient/dist/static/_nuxt
ディレクトリに書き出されます。
index.html内ものようになります。
これでDjangoでも静的ファイルとして認識してくれそうです。
Djangoの設定
さて、Django側でも設定が必要です。
herokuのドキュメントでは静的ファイルを使うにはwhitenoiseを使うといいよと書いてあるので使ってみます。
devcenter.heroku.com
settings.py
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) MIDDLEWARE = [ (その他のミドルウェア) 'whitenoise.middleware.WhiteNoiseMiddleware', ] STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, 'client/dist/static'), ) STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
ポイントはSTATICFILES_DIRS
にNuxt.jsが書き出すjavascriptへのパスを指定していることです。
これでDjangoでの静的ファイルの設定は完了です。
ちなみにherokuのpython buildpackではビルド時にcollectstaticが走って、静的ファイルを一箇所に集めてくれます。今回の例ではSTATIC_ROOT
で指しているstaticfiles
ディレクトリです。
herokuはきめ細かいですね。
DjangoでSPAのページルーティングに対応させる
DjangoでのルーティングをSPAのページにも適用させましょう。
SPAのURLになったらindex.htmlを表示させればいいのですが、SPAのURLを管理するのは面倒なので、Djangoが管理するURL以外だったらindex.htmlを表示させます。
urls.py
urlpatterns = [ path('admin/', admin.site.urls), path('api/', views.api), re_path('', TemplateView.as_view(template_name='index.html')), ]
こんな感じで、/admin/
と/api/
以外のパスはindex.htmlを表示させます。
index.htmlの場所をDjangoに教えておきましょうね。
settings.py
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'client/dist')],
TEMPLATES.DIRS
にindex.html
のパスを書いておけばOKです。
views.apiは適当につくっておきましょう。
views.py
from django.http import JsonResponse def api(request): return JsonResponse({'messsage': 'hello'})
単純なjsonを返すだけにしておきます。
ローカル環境で確認
ここまでをローカル環境で確認しておきましょう。
$ cd client $ yarn generate $ cd .. $ pipenv shell $ python manage.py runserver
http://127.0.0.1:8000/
Nuxt.jsのページが表示されます。
http://127.0.0.1:8000/admin/
Djangoの管理ページが表示されます。
http://127.0.0.1:8000/api/
先程作ったjsonが表示されます。
http://127.0.0.1:8000/xxxxxx
Nuxt.jsのエラーページ(not found)を表示されます。
静的ファイルの読み込みやルーティングはうまくいっていますね。
Heroku対応
Heroku環境にあわせる
django_herokuを使って、settings.py
に以下のように書いておけば、いい感じに対応してくれます。ALLOWED_HOSTSとかを。
settings.py
django_heroku.settings(locals())
Buildpacks適用
Buildpacksはheroku/pythonとheroku/nodejsを使います。
まずheroku/nodejsでNuxt.jsのビルドをしてから、heroku/pythonでDjangoのビルドをします。
$ heroku login $ heroku create $ heroku buildpacks:set heroku/python $ heroku buildpacks:add --index 1 heroku/nodejs
heroku/nodejsを先に起動させるように--index 1
をつけてあります。
Herokuではプロジェクトルートディレクトリを見てBuildpacksを適用します。
Djangoはルートディレクトリに作ったので問題ないのですが、Nuxt.jsはサブディレクトリ(client)に作ったので、細工をしましょう。
heroku/nodejsがやっていることはpackage.json
を見て、パッケージインストールとビルドをしているだけです。
そこで、ルートディレクトリにフェイクのpackage.json
をおいておきます。
package.json
"scripts": { "postinstall": "cd client; yarn install --production=false", "build": "cd client; yarn nuxt-ts generate" }
--production=false
は、Heroku上ではNODE_ENV=production
で動くのですが、Nuxt.jsのビルドにはdevDependencyを使うので、devDependencyもインストールさせるためのオプションです。
heroku/nodejs buidpackにyarnを使っていることを教えるために、yarn.lockを作っておきます。
$ yarn install
Procfile
最後にデプロイ後に起動させるコマンドをProcfile
に書いておきます。
Node.jsやRailsプロジェクトでは不要ですが、Djangoでは必要になります。
Procfile
web: gunicorn hello.wsgi
デプロイ
ここまでの内容をgit commit
したら、いよいよHerokuにデプロイです。
$ git push heroku master $ heroku open
https://xxxx.herokuapp.com/
Nuxt.jsのページが表示されます。
https://xxxx.herokuapp.com/admin/
Djangoの管理ページが表示されます。
https://xxxx.herokuapp.com/api/
jsonが表示されます。
https://xxxx.herokuapp.com/xxxxxx
Nuxt.jsのエラーページ(not found)を表示されます。
オールOKです!
ここまでのソースコードはこちらです。
↓ ↓ ↓
github.com

- 作者:横瀬 明仁
- 発売日: 2018/08/26
- メディア: オンデマンド (ペーパーバック)