12
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

Organization

Nuxt で Express を使いときの reload 問題。

はじめに

こんにちは。今日も明治大学 advent calendarやっていきます。

アドベントカレンダー忘れてて、数週間前に貯めておいた記事でどうにか繋ぐ。

Reload 問題

nuxtとapi serverを実装して最高のSSR applicationを作りたいぜ!なんて思うでしょ?
でもね、ググって出てくるnuxt & api serverって思ったより微妙なんだ。どれも、開発時のreloadが気持ちよくない。

nuxt & expressでググって良く出てくるのは以下の2つのパターン。
1. nuxt as an express middleware
2. express as a nuxt middleware

1. nuxt as an express middleware

まずはこっち。nuxtが提供しているexpress middlewareを使うバターン。

Example

コードはこれ。見ての通り、 Nuxt builder middlewareをexpressで使う。api serverをNodemonとかでwatch&reloadするような開発と非常に相性が悪い。

const express = require('express')
const { Nuxt, Builder } = require("nuxt")
const app = express()

let config = require("./nuxt.config.js")
config.dev = !(process.env.NODE_ENV === "production")
const nuxt = new Nuxt(config)

if (config.dev) {
    const builder = new Builder(nuxt)
    builder.build()
}

const api = require("./api")

app.use("/api", api)
app.use(nuxt.render)

Pros and Cons

  • Pros
    • 実装が楽
  • Cons
    • api server開発時にnuxtを最ビルドする必要があり、DX(Developer Experience)が最悪。

2. express as a nuxt middleware

次はこっち。Nuxtのmiddlewareとしてexpressを使うパターン。

Example

なんと、NuxtがserverMiddlewareなる機能を提供してくれていて、それを使うと本当に簡単にapi serverを実装できる!しかもね、api serverの変更をwatchしてくれて、server processだけをreloadしてくれるスグレモノ。これこそ、何も考えずにssr開発できるのがnuxtの強みなんだよな。

でもね、このexampleの場合、/api/index.jsの変更しかwatchしてくれないんだ。だから、./routes/usersを変更してもserverがreloadされない。

んーーーーーーーーーーーー、、、惜しいっっっ

nuxt.config.js
module.exports = {
  // ...
  serverMiddleware: [
    // API middleware
    '~/api/index.js'
  ]
}
/api/index.js
const express = require('express')

// Create express instnace
const app = express()

// Require API routes
const users = require('./routes/users')

// Import API Routes
app.use(users)

// Export the server middleware
module.exports = {
  path: '/api',
  handler: app
}

Nuxtならできるだろ?設定でなんとかできないのか?

ということで、Nuxtのソースコードを眺めてみる。ここは、serverMiddlewareのwathcerの部分。

一応、watch optionでwatchするファイルやディレクトリを指定することができる。が、Clientをwatchするときにも同じoptionを使うので、serverだけwatchしたいなんて要望には沿わない。

結局、できなかった

nuxt github: builder.js

  watchServer() {
    const nuxtRestartWatch = concat(
      this.options.serverMiddleware // ← ここ
        .filter(isString)
        .map(this.nuxt.resolver.resolveAlias),
      this.options.watch.map(this.nuxt.resolver.resolveAlias), // watch は watchClient() と共有
      path.join(this.options.rootDir, 'nuxt.config.js')
    )

    this.watchers.restart = chokidar
      .watch(nuxtRestartWatch, this.options.watchers.chokidar) 
      .on('change', (_path) => {
        this.watchers.restart.close()
        const { name, ext } = path.parse(_path)
        this.nuxt.callHook('watch:fileChanged', this, `${name}${ext}`)
      })
  }

Pros and Cons

  • Pros
    • 実装がめっちゃ楽
  • Cons
    • 一応、api server only reloadはできるけど、entry pointのみのreloadになるので、完璧なDXとは言えない。

Temporary Solution

とはいえ、わざわざ、nuxt serverとapi serverを分離させてproxyで...なんて面倒なことやりたくない。nuxtがサポートしてくれるまで寝て待てばいいのだろうけど、ずっと寝ちゃいられない。ということで、苦肉の策だけど、即時的というか一時的な解決策としては、これぐらい。

webpackとかでbuildしたやつをserverMiddlewareに設定する

あれ、nuxt serverとapi serverを分離させた方が良いんじゃ、、

おわりに

もしかして良い方法ってあるの?あったら教えてください☺️
自分だったら頑張って、分離させてproxyでほげほげしちゃうかも。面倒だけどね。typescriptも使いたいし。

追記

本当は Rust で Parser Combinator 実装!みたいな内容にしたかったんだけど、Ownership と Lifetime に苦しまされていて何も実装できなかった。Rust でコンビネーターはキツくない??

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
12
Help us understand the problem. What are the problem?