LoginSignup
42
26

More than 5 years have passed since last update.

Nuxt.js & Netlify-Lambda で Serverless SPA 開発

Posted at

netlify で lambda が使えるようになったので、静的サイト & シンプルなAPI のWebサイトを構成してみようと思い、実践的な内容も踏まえて挑戦してみました。

Lambda On Netlify の詳細に関しては以下を参照。

今回は Connpass API を使って、 Connpass のイベント情報を表示するSPAを作成します。

環境構築

とりあえず nuxt テンプレートを生成

bash
$ vue init nuxt-community/pwa-template myapp
$ cd myapp
$ npm i 

必要モジュールをインストール

$ npm i babel-core babel-loader netlify-lambda
$ npm i axios

Lambda 関数の作成

まずは API から作っていきます。裏で Connpass API を叩く部分は再利用可能にしたいので、Lambda のエントリからは分離しておきます。

$ mkdir src
$ mkdir service
$ touch src/events.js
$ touch service/http.js
events.js
const axios = require('axios')

exports.api = () => {
  const url = 'https://connpass.com/api/v1/event/'
  const params = {
    series_id: 1712 // PUT YOUR SERIES ID
  }
  return axios.get(url, {params}).then((result)=>{
    return result.data.events
  })
}

exports.handler = (event, context, callback) => {
  exports.api().then((result) => {
    console.log(result)
    callback(null, {
      statusCode: 200,
      headers: {
        'Content-type': 'application/json'
      },
      body: JSON.stringify(result)
    })
  })
}

netlify-lambda serve src で疎通を確認してください。

環境変数を利用する

series_id などを 柔軟に変更したいので、dotenv-module を利用して環境変数を参照します。

$ npm i @nuxtjs/dotenv

.env ファイルを作成して、イベントIDを環境変数で記録します。

CONNPASS_SERIES_ID=1712

.env は忘れずに ignore に入れておきましょう。

ただ、nuxt 側では、モジュールを利用して.env を参照出来るものの、 netlify-lamdba serve では .env を認識しません。

Nuxtのモジュールの中身は dotenv が入るだけなので、以下のコマンドで .env を食わせながら、netlify-lamdba コマンドを実行出来ます。

$ node -r dotenv/config ./node_modules/.bin/netlify-lambda serve src

準備ができたところで、service/http.jsの中身を以下の様に書き換えます。

events.js
exports.api = () => {
  const url = 'https://connpass.com/api/v1/event/'
  const params = {
    series_id: process.env.CONNPASS_SERIES_ID
  }
  return axios.get(url, {params}).then((result)=>{
    return result.data.events
  })
}

セキュアな情報でもないので、配信時の環境変数は、netlify.toml に記述しておきます。

netlify.toml
[build]
  Command = "npm run build"
  functions = "functions"
  publish = "dist"
[context.production]
  [context.production.environment]
    CONNPASS_SERIES_ID="1712"

toml 側に数値を書くとエラーになる?っぽいので、 ""で囲っておく必要があります。

npm run build をビルドコマンドにしているため、 package.json 側には

  "scripts": {
    ....
    "build": "touch .env && netlify-lambda build src && nuxt generate",
    ....
  },

のような設定を入れておきます。

以上を netlify 側に deploy してうまいこと動くこと確認できたらOKです。

Nuxt 側の開発

次は、Nuxt 側の開発です。

まずローカル環境で API を Proxy したいので, @nuxtjs/proxy を入れます。

$ npm i @nuxtjs/proxy

modules に @nuxtjs/dotenv も合わせて設定を追加します。

nuxt.config.js
  modules: [
    '@nuxtjs/dotenv',
    '@nuxtjs/proxy',
    ....
  ],
  proxy: {
    '/events': 'http://localhost:9000'
  }

とりあえず 簡単な store を作成します。

store/index.js
import axios from 'axios'

export const state = () => ({
  events: []
})

export const getters = {
  eventById: (state) => (id) => {
    for (let event of state.events) {
      if (event.event_id === parseInt(id)) {
        return event
      }
    }
    return false
  }
}

export const mutations = {
  ADD_EVENTS (state, events) {
    state.events = events
  }
}

export const actions = {
  async LOAD_EVENTS ({commit}) {
    const result = await axios.get('/events', {
      baseURL: process.env.FRONT_API_URL
    })
    commit('ADD_EVENTS', result.data)
    return result.data
  }
}

process.env.FRONT_API_URL は API のURL です。とりあえず以下のように .env に追加します。

.env
CONNPASS_SERIES_ID=1712
FRONT_API_URL=http://localhost:3000

netlify.toml 側では以下のようにします。

netlify.toml
[context.production]
  [context.production.environment]
    CONNPASS_SERIES_ID="1712"
    FRONT_API_URL="https://{YOUR_SITE_NAME}.netlify.com/.netlify/functions"

.envの内容は ブラウザ側では動作しないため、 nuxt.config.js に以下のような指定をして、コンパイルのJS内に組み込んでもらいます。

nuxt.config.js
  env: {
    FRONT_API_URL: process.env.FRONT_API_URL
  },

トップのイベント一覧ページの .vue は次のような script 構成になります。

page/index.vue
  export default {
    data () {
      return {}
    },
    computed: {
      events () {
        return this.$store.state.events
      }
    },
    async fetch ({store}) {
      await store.dispatch('LOAD_EVENTS')
    }
  }

イベントの個別ページは次のような形です。

page/event/_eventId.vue
  export default {
    data () {
      return {}
    },
    computed: {
      eventId () {
        return this.$route.params.eventId
      },
      event () {
        const event = this.$store.getters.eventById(this.eventId)
        return event
      },
    },
    async fetch ({store, params}) {
      const event = store.getters.eventById(params.eventId)
      if (event === false) {
        await store.dispatch('LOAD_EVENTS')
      }
    }
  }

注意が必要なのは、 fetch 内で this.$store などが使えない点です。

generate 向けの設定

最後に動的なルートを描画出来るように、generate オプションを指定します。

nuxt.config.js
  generate: {
    routes (callback) {
      const {api} = require('./src/events')
      require('dotenv').config()
      return api().then((result) => {
        const routes = result.map((event) => {
          return `/event/${event.event_id}`
        })
        callback(null, routes)
      }).catch(callback)
    }
  },

@nuxt/dotenv は公式サイトにも以下の記述があるように、ビルドフェーズで動作しないため、明示的にコレを読み込む必要があります。

The dotenv-module won't overload the environment variables of the process running your build.

以上で構築は完了です。

うまいこと レンダリングされた、静的ページが複数生成されると思います。

後で暇があったらデザイン整えて netlify buttonにして公開しようと思います。
(誰かやる気の出るデザインを下さい。)

42
26
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
42
26