Vue-CLI-Plugin-Express を使って Vue + Express スタックを実現する

Vue.js でアプリを作りつつ、同時にバックエンド API も作りたいな、と思うと、Nuxt.js を使って実装しちゃう方法がすぐ思いつくかもしれない。

しかし、

という時に、Nuxt.js だと縛りが強くなってしまって若干使いづらい。

そこで見つけたのが、Vue-CLI-Plugin-Express という Vue CLI 用のプラグイン。既存の Vue CLI ベースのプロジェクトに、Express サーバを統合してくれるというモノだ。早速使ってみよう。

目次

Vue CLI でプロジェクトを作る

まずは Vue CLI を使ってプロジェクトを作る。@vue/cli のバージョンは v4.4.4 を使用。

$ vue create practice-vue-express

TypeScript ベースで実装するため、「Manually Select Features」を選択し、Babel・TypeScript・CSS Pre-processors を有効にする。「Linter / Formatter」や「UnitTesting」、「Router」「Vuex」などはお好みで。

雛形ができたら、

$ cd practice-vue-express/
$ npm run serve

http://localhost:8008/ に簡易サーバが立ち上がることを確認しよう。

Vue-CLI-Plugin-Express をインストールする

雛形プロジェクトの動作確認ができたら、プラグインをインストールする。

$ vue add express

# 以下2つの質問に答える
? Should serve vue app? Yes
? Where will be located your server? ./srv

🚀  Invoking generator for vue-cli-plugin-express...
✔  Successfully invoked generator for plugin: vue-cli-plugin-express

インストールコマンドを叩くと2つの質問が問われる。「Should serve vue app?」は Yes と答え、「Where will be located your server?」はデフォルトの ./srv のまま答える。

このようにすると、

といった変化が出る。

試しにライブリロード開発してみる

./srv/index.js を見ると、export default に囲まれているものの、何やら Express でお馴染みの app 変数が用意されているようだ。コメントアウトでサンプルコードも書かれているので、試しに次のようにアンコメントしてみよう。

import express from 'express';
// import socketIO from "socket.io";

export default (app, http) => {
  // ↓ ココからアンコメント
  app.use(express.json());
  
  app.get('/foo', (req, res) => {
    res.json({msg: 'foo'});
  });
  // ↑ ココまでアンコメント
  
  //
  // app.post('/bar', (req, res) => {
  //   res.json(req.body);
  // });
  // 
  // optional support for socket.io
  // 
  // let io = socketIO(http);
  // io.on("connection", client => {
  //   client.on("message", function(data) {
  //     // do something
  //   });
  //   client.emit("message", "Welcome");
  // });
}

このようにしたら、

$ npm run express

を叩いてみる。すると http://localhost:3000/ に簡易サーバが起動する。先程 /foo というパスのルーティングをアンコメントで実装したので、

にアクセスすると、{"msg":"foo"} というレスポンスが得られる。./srv/index.js の実装のとおりだ。

さて、ココで、$ npm run express を実行したターミナルウィンドウはそのままに、別タブで

$ npm run serve

を実行してみよう。コチラは元々の Vue CLI が提供する、フロントエンド環境の簡易サーバが立ち上がる。ポート番号は先程の 3000 番ではなく 8080 版で立ち上がっていることに留意。

にアクセスすれば Vue アプリの画面が開くのはそのとおりだが、ココで

にアクセスしてみよう。すると、フロントエンド側の 8080 ポートであるにも関わらず、バックエンド側の /foo の情報がレスポンスされる。8080 ポートの簡易サーバがプロキシの役割を果たして、バックエンド側の簡易サーバにアクセスしているのだ。

つまり、Vue アプリ内では、オリジンを指定せず /foo への Ajax 通信処理を実装すれば、2つの簡易サーバを立ち上げてそれぞれでライブリロード開発ができるワケである。

プラグインの説明では、コレを「Fallback」と表現している。

本番ビルドの流れ

プラグインが簡単にバックエンドを用意してくれて、Vue アプリと同一サーバで動いているかのようなフォールバック機能も提供してくれることは分かった。

では、本番ビルドはどのように行うかというと、まず

$ npm run build

でフロントエンド側のコードを ./dist/ にビルドして配置しておく。

そしたら

$ npm run express:run

で Production 用の Express サーバを起動する。コチラのコマンドはライブリロードは行わない他、./dist/ 配下の資材を参照するので、8080 ポートで何が動いていようと無視される。使用するポートは $ npm run express の時と同じ 3000 番ポートなので、

にアクセスすれば Vue アプリが見えるし、

にアクセスすれば Express で定義したルーティングに基づき JSON が返されるという具合だ。

Express サーバを TypeScript で実装したい

さて、コレでひとまずプラグインの動作は確認できたが、Express サーバのコードは ./srv/index.js ということで、JavaScript 形式になっている。コレを TypeScript で実装したい場合はどうしたらいいかというと、単純に拡張子を .ts に変えるだけで良い。

試しにコード中に TypeScript 記法を混ぜ込んで、動作を確認しよう。

app.get('/foo', (req, res) => {
  const fooMessage: string = 'foo';
  res.json({ msg: fooMessage });
});

こんな感じ。

VSCode 上での補完などを効かせるには、tsconfig.json を開き、include: の配列の中に

を追加してやれば良いだろう。

コレで実際に動かしてみると、ちゃんと TypeScript と解釈して動作していることが分かる。

# どちらも動く
$ npm run express
$ npm run express:run

内部的には ts-node を使って動いているのかな?

以上

ベースとなる Vue CLI アプリに、軽く Express サーバを立てて API を組み合わせたくなった時に丁度良いプラグインであろう。