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

More than 1 year has passed since last update.

posted at

updated at

NUXT.jsでSSRする場合の落とし穴

NUXT.js×REST API×Typescript×SSR

NUXT.jsはSSRの設定自体はmode: 'universal', とするだけでいいのですが、SSRとSPAで気を使って実装しないと、データの取得などで、どっちかでは動く、動かない問題が出てきます。
結構ハマったのでメモ。

:skull: 問題

SSR時だけAPIレスポンスが取得できない。

SPA時はAPIレスポンスがちゃんと返ってきて問題ないのだが、
SSR時はリクエストはされているのに、APIレスポンスが返ってこないようなエラーが起こった。

:zap: 原因

SSRする場合、asyncDataのライフサイクルでAPIのリクエストをする必要があると思います。
その時、proxyとかを使っているとSSR時のみAPI取得ができません。
nuxt-linkの遷移ならうまくいくのに、画面リロードしたらエラーでるみたいなケースです。

※SSRのときはproxyを使う必要がありません。

:tada: 解決

axiosのbaseURLをブラウザ時とSSR時で分けるだけだった
baseURL: process.browser ? '' : ‘https://api.endpoint.com’

process.browser はnuxtならデフォルトで取得できます。

今回は下記モジュールを使っている前提です。
- axios(axios-moduleを使わずaxiosを使いたい)
- @nuxt/proxy

※ axios-moduleを使わない理由は下記記事と同様の理由
:fire: replace axios-module to axios - axios-moduleをaxiosにreplaceしたはなし

APIクライアント

/plugins/api/utils/clinet.ts

import axios from 'axios'

const instance = axios.create({
  baseURL: process.browser ? '' : process.env.API_HOST, //←これを追加。ブラウザのときは 空 もしくは/apiから始まるようにしないとproxyが効かずフロントから取得できないです。
  withCredentials: process.env.NODE_ENV !== 'production',
  headers: {
     'X-Requested-With': 'XMLHttpRequest',
     'Access-Control-Allow-Origin': '*'
  },
  timeout: 20000
})
export const client = instance

:raising_hand:下記は補足

@nuxt/proxyの設定

$ yarn add @nuxt/proxy でインストールして

nuxt.config.ts

const config: NuxtConfiguration = {
  mode: 'universal',

  modules: [
     '@nuxtjs/proxy'
  ],
  proxy: { //  /apiで始まるアクセスを http://docker.for.mac.localhost(APIサーバーのエンドポイント)に置き換える
    '/api': {
      target: ` http://docker.for.mac.localhost`,
      changeOrigin: true,
      pathRewrite: {
        '^/api/': '/api/'
      }
    }
  }
}

APIメソッド

/plugins/api/auth.ts

import { client as httpClient } from '@/plugins/api/utils/client'

// import types
import * as types from '@/types/AuthUser'

/**
 * api/auth_user
 * @param null
 */
export const getAuthUser = () => {
  return httpClient
    .get<types.GetAuthUserResponse>(`/api/auth_user`)
    .then((response) => response)
}

API型定義

/types/AuthUser.ts

export interface AuthUser {
  id: number
  name: string
  token: string
}

export interface GetAuthUserResponse {
  data: {
    authUser: AuthUser
  }
}

APIの呼び出し

pages/index.vue


<script lang="ts">
import { Component, Vue } from 'nuxt-property-decorator'
import { AuthUser } from '@/types/AuthUser'
import { getAuthUser } from '@/plugins/api/auth'

@Component
export default class Index extends Vue {
  authUser!: AuthUser

  async asyncData({ app, error }) {
    const result = await getAuthUser()
      .then((res) => {
        return { authUser: res.data.auth_user }
      })
      .catch((e) => {
        console.log(e)
        error({
          statusCode: 500,
          message: 'サーバー側でエラーが発生しました'
        })
        return
      })
    return result
  }
}
</script>
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
63
Help us understand the problem. What are the problem?