Nuxt.js + Netlify Functions を TypeScript で構築する


この記事の目標

  • Nuxt.js + TypeScriptで実装
  • Netlifyにホスティング
  • Netlify FunctionsをTypeScriptで実装
  • Nuxt.jsからNetlify Functionsを呼び出す

動作確認環境

項目 バージョン
macOS 10.13.6 (High Sierra)
node 13.8.0
npm 6.13.7

ソースコード

1. Nuxt.js

まず、フロントエンド環境から構築していきます。

ボイラーテンプレート生成

create-nuxt-appを使います。

2020年3月18日にリリースされたv2.15.0から、TypeScriptをサポートしているので、質問に答えるだけで、簡単にTypeScript環境を構築できます。

$ npx create-nuxt-app nuxt-netlify-ts
  • 言語はTypeScriptを選択
  • TypeScriptのruntimeは@nuxt/typescript-runtimeを選択
  • Axiosのモジュールを追加
  • レンダリングモードはSPA
  • あとはお好みで
? Project name nuxt-netlify-ts
? Project description My super Nuxt.js project
? Author name nishitaku
? Choose programming language TypeScript
? Choose the package manager Yarn
? Choose UI framework Vuetify.js
? Choose custom server framework None (Recommended)
? Choose the runtime for TypeScript @nuxt/typescript-runtime
? Choose Nuxt.js modules Axios, DotEnv
? Choose linting tools ESLint, Prettier
? Choose test framework None
? Choose rendering mode Single Page App
? Choose development tools jsconfig.json

以下Lintエラーが出力されますが、無視してOKです。

$ eslint --ext .js,.vue --ignore-path .gitignore . --fix

/Users/takuro/workspace/ojthv2/nuxt-netlify-ts/nuxt.config.js
  80:12  error  'config' is defined but never used. Allowed unused a
rgs must match /^_/u  @typescript-eslint/no-unused-vars
  80:20  error  'ctx' is defined but never used. Allowed unused args
 must match /^_/u     @typescript-eslint/no-unused-vars

✖ 2 problems (2 errors, 0 warnings)

error Command failed with exit code 1.

動作確認

開発サーバ起動

$ cd nuxt-netlify-ts
$ yarn dev

http://localhost:3000にアクセスして正常に表示されることを確認。

2. Netlifyにデプロイ

次に、生成したボイラーテンプレートを、Netlifyにデプロイします。

NetlifyはGitHubリポジトリと紐付けておくことで、PushやMergeをトリガーに、自動的にビルド&デプロイしてくれる機能があるため、それを使っていきます。

GitHubリポジトリ作成

Gitに関してはざっくりとコマンドのみ。

$ git remote add origin (リポジトリURL)
$ git add .
$ git commit -m "generated by create-nuxt-app/2.15.0 darwin-x64 node-v13.8.0"
$ git push origin master

NetlifyとGitHubリポジトリを紐付け

  • Create a new site
    • Continuous Deployment
      • GitHubを選択
    • Continuous Deployment: GitHub App
      • 先程のGitHubリポジトリを選択
    • Basic build settings
      • Build command:yarn build
      • Publish directory:dist

動作確認

デプロイ完了後、独自に割り当てられたhttps://(Netlifyドメイン)にブラウザからアクセスし、開発サーバのときと同じ画面が表示されることを確認。

3. Netlify Functions

Netlifyが提供するFaaS (Function as a Service)

Freeプランで、125,000リクエスト/月100時間/月使えます。

ベースはAWS Lambdaですが、シンプルな設定ですぐに使い始められます。

環境構築

netlify-lambdaを使います。

$ yarn add -D netlify-lambda

次に、TypeScriptで記述するためのライブラリを追加。

$ yarn add -D @babel/preset-typescript @types/aws-lambda

netlify-lambdaのbabelの設定を上書きするために、新たに.babelrcをルートに追加。

.babelrc
{
  "presets": [
    "@babel/preset-typescript",
    "@babel/preset-env"
  ],
  "plugins": [
    "@babel/plugin-proposal-class-properties",
    "@babel/plugin-transform-object-assign",
    "@babel/plugin-proposal-object-rest-spread"
  ]
}

開発サーバを起動するためには、netlifyの設定ファイルが必要になります。

また、NetlifyはGitHubリポジトリにnetlify.tomlが存在する場合、GUIでの設定内容よりも優先します。そのため、Functionsの設定だけでなく、ホスティングの設定も合わせて記載しておく必要があります。

netlify.toml
[build]
  Command = "yarn build"
  functions = "functions/dist"
  publish = "dist"

package.jsonに、開発サーバ起動スクリプトを追加。

package.json
{
  "scripts": {
    "dev:functions": "netlify-lambda serve functions/api",
  },
}

実装

ディレクトリfunctions/apiを追加し、サンプル用の関数を実装。

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
) => {
  console.log(`hello invoked`)
  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)
}

動作確認

開発サーバを起動。9000番ポートで起動します。

$ yarn dev:functions

curlでレスポンスを確認。

$ curl "http://localhost:9000/.netlify/functions/hello"
{"msg":"Hello world 0","requestId":"dummy","params":{}}

因みに、http://localhost:9000/helloでも同様の結果が返ってきます。

4. Netlify Functionsのデプロイ

ホスティングと同様、GitHubリポジトリへPushするだけで、自動的にNetlify環境へ反映されます。

GitHubリポジトリへのPush

まず、Functionsのビルド設定を追加。

package.json
{
  "scripts": {
    "build": "netlify-lambda build functions/api && nuxt-ts build",
  },
}

GitHubにPushし、

動作確認

デプロイ完了後、curlでレスポンスを確認。

$ curl "http://(Netlifyドメイン)/.netlify/functions/hello"
{"msg":"Hello world 0","requestId":"dummy","params":{}}

5. Nuxt.jsからNetlify Functionsを呼び出す

NetlifyにデプロイしたNuxt.jsの画面から、Netlify Functionsの関数を呼び出します。

CORSエラー対応

開発環境では、Nuxt.jsのポート番号(3000)とnetlify-lambdaのポート番号(9000)が異なるため、そのままだとCORSエラーが発生します。

回避策として、@nuxtjs/proxyのリバースプロキシ機能を使うことで、http://localhost:3000/.netlify/functions/helloへのアクセスを、http://localhost:9000/.netlify/functions/helloへ転送します。

@nuxtjs/proxyをインストール。

$ yarn add @nuxtjs/proxy

nuxt.config.jsmodules@nuxtjs/proxyを追加

nuxt.config.js
export default {
  modules: [
    '@nuxtjs/proxy'
  ],
}

nuxt.config.jsproxyを追加

nuxt.config.js
export default {
  proxy: {
    '/.netlify/functions': {
      target: 'http://localhost:9000'
    }
  }
}

Axiosの設定

@nuxtjs/axiosbaseURLに何も設定しなかった場合、デフォルトでhttp://localhost:3000となってしまいます。

そのため、https://(Netlifyドメイン)からhttps://(Netlifyドメイン)/.netlify/functionsを呼び出してほしいのに、http://localhost:3000/.netlify/functionsを呼び出してしまいます。

これを回避するため、nuxt.config.jsaxiosbaseURLを設定します。

nuxt.config.js
export default {
  axios: [
    baseURL: '/'
  ],
}

ソースコード修正

動作確認用のコードをindex.vueに埋め込む。

index.vue
<script>
export default {
  // 追加
  async mounted() {
    try {
      const response = await this.$axios.$get('.netlify/functions/hello')
      console.log(`respones=${JSON.stringify(response)}`)
    } catch (err) {
      console.error(err)
    }
  }
}
</script>

動作確認

$ yarn dev // Nuxt.js開発サーバ起動
$ yarn dev:functions // netlify-lambda開発サーバ起動

http://localhost:3000にブラウザからアクセスし、コンソールログにFunctionsを呼び出した結果が表示されていることを確認する。

さらに、GitHubにPushし、デプロイ完了後、ブラウザからNetlifyドメインへアクセスし、同様にコンソールログを確認する。

さいごに

FaaS (Functions as as Service)を使ったサーバレスアーキテクチャは、ここ数年でかなり使いやすくなってきている印象です。

今回のNetlify Functions以外にも
- Google Cloud Functions
- AWS Lambda
- IBM Cloud Functions
- Azure Functions
- Twilio Functions
など、各クラウドベンダが提供しているFaaSもあるので、それらと比較してみると面白そうです。

また、Netlifyには他にも便利な機能があるみたいなので(Formsとか認証とか)、Netlify CLIと一緒に今後使ってみたいと思います。

Reference