Tamanyan.me | たまにゃんのエンジニアブログ

JAMStackなアプリを開発するには動的コンテンツの表示も可能にしなくてはならない。そのためにはマイクロAPIからデータを取得してくる必要がある。Netlifyはその機能を実現するためにFunctionsを機能として備えている。また、NetlifyはサードパーティのライブラリをインストールすることなくSplit TestingでA/Bテストを行う事ができる。しかもGitのブランチレベルでのトラフィックの振り分けが可能だ。

Netlifyのチュートリアルについて知りたければ以前のNuxt.js (TypeScript) on Netlifyの良さを広めたいの記事を参照してほしい。

目次

  1. Netlify Functionsとは
  2. Netlify Split Testingとは
  3. Nuxt TypeScriptのサンプルプロジェクトをClone
  4. netlify-lambdaのインストール
  5. FunctionsをTypeScriptで書く
  6. Nuxt.jsからFunctionsの呼び出し
  7. Split TestingでA/Bテストをやってみる
  8. 最後に

Netlify Functionsとは

裏はAWS Lambdaになっているようだが、我々が特にAWSを意識する事はない。価格は公式から引用させてもらうと

All Netlify sites include 125,000 function requests per month, and 100 hours of run time per month. Above that, billing scales with your usage.

125,000リクエスト/月、実行時間が100時間まで無料らしい。あとは従量課金制だ。個人的にAWS LambdaやCloud FunctionsはGB秒という確保するメモリと実行時間を合わせた単位で課金されるので、あまり直感的ではないが、Netlify Functionsはリクエスト数と実行時間のみで課金されるようだ。

Netlify Split Testingとは

段階的ロールアウトやA/B TestingなどUIから簡単に設定できる機能。まだBETA(Feb 5 2019時点)だが、個人的に今回一番推したい機能。

A/B Testing

Nuxt TypeScriptのサンプルプロジェクトをClone

既にサンプルは用意したので、以下のレポジトリからCloneするかForkして、Github上にプロジェクトを作っておく。masterではなく、functionsのブランチを今回利用するので注意

tamanyan/nuxt-ts-netlify at functions Nuxt TypeScript on Netlify. Contribute to tamanyan/nuxt-ts-netlify development by creating an account on GitHub. github.com
git clone https://github.com/tamanyan/nuxt-ts-netlify.git <project-name>
cd <project-name>
yarn or npm install

netlify-lambdaのインストール

関数の実行やビルドなどしたい場合はnetlify/netlify-lambdaが便利。TypeScriptのビルドなども対応しているようだ。サンプルプロジェクトでは全て設定が完了しているので特に何もする必要はない。念の為netlify.tomlの設定部分に関してのみ説明しておくと、commandはビルドコマンド、functionsにLambda関数を置いておくようにする。publishは公開ディレクトリ。

# example netlify.toml
[build]
  command = "yarn generate" # generate cmd
  functions = ".lambda" # netlify-lambda reads this
  publish = "dist" # The directory you want to publish

FunctionsをTypeScriptで書く

functionshello.tsにLambda関数を書く、ビルドすると.lambdaに出力されるようになっている。

functions/hello.ts

import { Handler, Context, Callback, APIGatewayEvent } from 'aws-lambda'

interface HelloResponse {
  statusCode: number
  body: string
}

export const handler: Handler = (
  event: APIGatewayEvent,
  context: Context,
  callback: Callback
) => {
  const params = event.queryStringParameters
  const response: HelloResponse = {
    statusCode: 200,
    body: JSON.stringify({
      msg: `Hello world ${Math.floor(Math.random() * 10)}`,
      requestId: context.awsRequestId || "dummy",
      params
    })
  }

  callback(undefined, response)
}

Nuxt.jsからFunctionsの呼び出し

ローカルでも呼び出せるようにaxiosproxyの設定をしておく。Nuxt.jsはポート3000で立ち上がるが、ローカルでnetlify-lambdaを使って関数を立ち上げると9000で起動するので、proxyを通しておくと便利。

nuxt.config.ts

  /*
   ** Nuxt.js modules
   */
  modules: [
    // Doc: https://github.com/nuxt-community/axios-module#usage
    '@nuxtjs/axios',
    '@nuxtjs/proxy'
  ],
  /*
  ** Axios module configuration
  */
  axios: {
    proxy: true // Can be also an object with default options
  },

  proxy: {
    '/.netlify/': { target: 'http://localhost:9000/' }
  },

ここでは、Functionsが呼び出せるかどうかだけを確かめるためにmountedで直接APIを叩いて関数を呼び出す。

src/pages/index.vue

<template>
  <section class="container">
    <div>
      <logo />
      <h1 class="title">
        Nuxt TypeScript Sample
      </h1>
      <h2 class="subtitle">
        {{ subtitle }}
      </h2>
      <div class="links">
        <a
          href="https://nuxtjs.org/"
          target="_blank"
          class="button--green"
        >Documentation</a>
        <a
          href="https://github.com/nuxt/nuxt.js"
          target="_blank"
          class="button--grey"
        >GitHub</a>
      </div>
    </div>
  </section>
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator'
import Logo from '../components/Logo.vue'


@Component({
  components: {
    Logo,
  }
})
export default class Index extends Vue {

  subtitle: string = 'Nuxt TypeScript Project!'

  async mounted() {
    // Call Netlify Functions
    const response = await this.$axios.$get('./.netlify/functions/hello')

    // Set subtitle from message
    this.subtitle = response.msg
  }
}
</script>

元々はNuxt TypeScript Project!が表示されていたが、APIのレスポンスからmsgを取得してHello World <number> になっている。

関数を呼び出した結果

Split TestingでA/Bテストをやってみる

masterとfunctionsの両方のブランチを使ってA/Bテストをやってみる。使い方は直感的で迷う事はない。ここではmasterに10%、functionsに90%振り分けてみる。

Split Testing

細かい部分として、今訪問者がどのブランチで出力されているのかを環境変数から取得することができる。これらはGoogle Analyticsなどの計測に使える。

nuxt.config.ts

export default {
  env: {
    BRANCH: process.env.BRANCH || 'undefined'
  }

src/pages/index.vue

export default class Index extends Vue {

  subtitle: string = 'Nuxt TypeScript Project!'

  async mounted() {
    // Call Netlify Functions
    const response = await this.$axios.$get('./.netlify/functions/hello')

    // Set subtitle from message
    this.subtitle = response.msg

    // Show current branch
    console.log('Your branch is', process.env.BRANCH)
    // -> master or functions
  }
}

最後に

NetlifyのFunctionsとSplit Testingを使って、A/Bテストまで試してみた。個人的な感想として、NetlifyはCDNにいろいろとサービスがくっついただけで、自動ビルド・デプロイ、Functions、Split Testingを使う程度ではNetlifyへのサービスロックインにはなりにくい。Netlify Functionsは裏がAWS Lambdaで移植性は高い(AWS Lambdaへのロックインはしょうがない)。A/Bテストに関してはアプリケーションのソースコードレイヤーのレベルではないため問題ない。安い(or 無料)だし、悩むくらいなら始めてみようぜといえるサービスになっている。


たまにゃん🇹🇭バンコクの機械学習エンジニア
バンコク(タイ)のソフトウェアエンジニア。元日本経済新聞社のiOSエンジニア。2018年からタイの自動車系の会社に転職。デジタルマーケティング、フロントエンド、データ基盤構築、機械学習辺りが最近の仕事。2019年からKaggleを始める。Kaggleソロ銀🥈1個。データサイエンス・ソフトウェアエンジニアリング・バンコクの事のついて情報発信する。