NUXT.js×REST API×Typescript×SSR
NUXT.jsはSSRの設定自体は mode: 'universal',
とするだけでいいのですが、SSRとSPAで気を使って実装しないと、データの取得などで、どっちかでは動く、動かない問題が出てきます。
結構ハマったのでメモ。
問題
SSR時だけAPIレスポンスが取得できない。
SPA時はAPIレスポンスがちゃんと返ってきて問題ないのだが、
SSR時はリクエストはされているのに、APIレスポンスが返ってこないようなエラーが起こった。
原因
SSRする場合、asyncDataのライフサイクルでAPIのリクエストをする必要があると思います。
その時、proxyとかを使っているとSSR時のみAPI取得ができません。
nuxt-linkの遷移ならうまくいくのに、画面リロードしたらエラーでるみたいなケースです。
※SSRのときはproxyを使う必要がありません。
解決
axiosのbaseURLをブラウザ時とSSR時で分けるだけだった
→ baseURL: process.browser ? '' : ‘https://api.endpoint.com’
※ process.browser
はnuxtならデフォルトで取得できます。
今回は下記モジュールを使っている前提です。
- axios(axios-moduleを使わずaxiosを使いたい)
- @nuxt/proxy
※ axios-moduleを使わない理由は下記記事と同様の理由
replace axios-module to axios - axios-moduleをaxiosにreplaceしたはなし
APIクライアント
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
下記は補足
@nuxt/proxyの設定
$ yarn add @nuxt/proxy
でインストールして
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メソッド
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型定義
export interface AuthUser {
id: number
name: string
token: string
}
export interface GetAuthUserResponse {
data: {
authUser: AuthUser
}
}
APIの呼び出し
<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>