Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
54
Help us understand the problem. What is going on with this article?
@Sr_Bangs

【入門】Netlify Functionsコトハジメ

前書き

Netlify Functionsの入門記事になります。
導入するにあたり色々調べても日本語の記事が少なかったり、Nuxt.jsと組み合わせてどうたらみたいなNetlify Functionsの事に絞られてない記事が多かったので自分用の備忘録も兼ねて。
前提としてJavaScriptチョット書ける、npm使える位の人からを対象とした記事です。

この記事を読むと

・Netlify Functionsの使い方が何となく分かるようになる。
・ローカルでNetlify Functionsのエミュレートが出来るようになる。
・Netlify Functionsを介して外部のAPIとやり取り出来るようになる。
・Netlifyでの環境変数の設定の仕方が分かるようになる。

Netlify Functions is 何

Netlifyが提供しているアドオンの1つ。
FaaS(Function as a service)と呼ばれるもので、機能(Functions)を実行できる環境を提供してくれるサービスです。
AWS Lambdaが基盤になっており、実行環境はNode.js 8.10がデフォルトで設定されています(執筆時点)。
10.x系を使いたい場合は、AWS_LAMBDA_JS_RUNTIMEの環境変数にnodejs10.xを指定すればオーバーライドできます。
AWS_LAMBDA_JS_RUNTIMEの環境変数で指定できる値は、AWS Lambda runtimeに記載されているものになります。)

Netlifyとリポジトリの連携

何はともあれNetlifyとリポジトリを連携させないと話が始まらないので連携させましょう。
連携させるだけならNetlifyのTOPページから流れに沿って進めるだけで特に難しいところもないので、細かい連携の方法については割愛します。
既にNetlifyと連携しているリポジトリを使うのであれば、Netlify Functionsのために新規でリポジトリを作る必要はありません。

環境構築

Netlifyの設定ファイルを作成する

Netlifyの設定ファイルであるnetlify.tomlファイルをルートディレクトリに作成します。
ここに書く設定はNetlifyの管理ページからでも設定できますが、後述するローカルでのエミュレートにこのファイルが必須になります。

netlify.toml
[build]
  command = "npm run build"
  functions = "dist/api"

command = Netlifyデプロイ時に実行されるコマンド
functions = Netlify Functionsのエンドポイント対象ファイルを格納するディレクトリ

ローカルでエミュレートできるようにする

netlify-lambdaを使えばローカルでNetlify Functionsのエミュレートをすることが出来ます。
これが無いと変更→コミット→デプロイ→確認というフローを毎回踏むことになって苦しいです。
後から導入することも出来ますが、Netlifyの設定の仕方によっては色々面倒なことになりかねないので最初に入れておくのがオススメです。

まずはルートディレクトリで

npm init

を実行してpackage.jsonを作成して

npm i netlify-lambda

でインストールしたら、package.jsonに実行用のスクリプトを追記します。

package.json
"scripts": {
  "dev": "netlify-lambda serve resources/api(ソースが格納されているフォルダ名)",
  "build": "netlify-lambda build resources/api(ソースが格納されているフォルダ名)"
}

netlify-lambda serve <folder-name>で、ローカルで確認できるサーバを立ち上げてくれると同時に、対象としているディレクトリの中身がコンパイルされて、netlify.tomlfunctionsで指定しているディレクトリに吐き出されます。

デフォルトで使用するポートは9000番ですが、--portオプションで変更することもできます。

port指定の例
"dev": "netlify-lambda serve resources/api --port 9001"

ここまでで最低限の環境構築は完了です。

Hello Worldが返ってくるAPIを作る

まずはサンプルとしてHello Worldの文字列が返ってくるAPIを作ってみます。
先ほどnetlify-lambdaの対象に設定したディレクトリ(今回の場合resources/api)の中にjsファイルを作成しましょう。
今回はとりあえずhello.jsという名前で作成しました。
このファイル名がエンドポイント名になるので実制作の際は命名に気をつけた方がいいです。
例:hello.js = /.netlify/functions/hello

hello.jsの中身

基本的には公式ドキュメントと全く同じような内容になります。

まずお作法として以下のようにhandlerメソッドをエクスポートする必要があります。

hello.js
exports.handler = function(event, context, callback) {
    // ここに中身の処理を記述
}

とりあえずHello Worldを返すだけなので、中身の処理はこれだけです。
callback関数の第2引数に、ステータスコードとbodyの情報を渡すだけ。
第1引数はエラーの時用なので今回はnullにしてます。

hello.js
callback(null, {
  statusCode: 200,
  body: 'Hello World'
});

ここまで終わったら

npm run dev

でローカルサーバーを立ち上げて、localhost:9000/.netlify/functions/helloにアクセスするとHello Worldが表示されているはずです。

eventパラメータに入っているもの

event
{
    "path": "リクエストのパス", 
    "httpMethod": "リクエストのメソッド(GETとかPOSTとか)"
    "headers": {ヘッダ情報}
    "queryStringParameters": {クエリパラメータの情報}
    "body": "リクエストの文字列"
    "isBase64Encoded": "リクエストがBase64でエンコードされているかどうか"
}

contextパラメータに入っているもの

Netlify Identityを使用している場合にユーザーのコンテキスト情報が返ってきます。(使用していない場合は空の配列)
私自身がNetlify Identityを使用したことがないのであまり詳しいことは分からないです、スミマセン。

asyncを使えば簡潔に書ける

asyncを使うことでより簡潔に書くことができます。
(実行環境をNodejs6.10に指定していると使えないので注意)

hello.js
exports.handler = async () => {
  return {
    statusCode: 200,
    body: 'Hello World',
  };
};

コミットして動いているか確認

ここまでの設定が終わったらコミットして本番でも動いているか確認して見ましょう。
デプロイが終わった後、管理ページのFunctionsの中にhello.jsの項目が出来ていたら無事に認識されています。
Functions___functionstest.png

hello.jsの項目をクリックするとログを見られる画面に遷移します。
Function_hello___functionstest.png

Function logのところに実行された履歴や、JS内にconsole.logを書いていればその内容が表示されます。
EndpointのURLにアクセスして、画面にHello Worldが表示されていれば本番でも問題なく動いています。

Netlify Functionsの中で外部APIを扱う

外部APIを使用する時にAPIのキーをフロント側に持たせたくないというのはあるあるだと思うのですが、Netlifyの環境変数と組み合わせれば幸せになれます。

環境変数の設定(Netlify側)

環境変数の設定はSettings > Build&deploy > Environmentから行えます。
Build___deploy___Settings.png

ここでkeyvalueを設定しておけば、process.env.KEYのような感じで環境変数を取得することが出来ます。

環境変数の設定(ローカル側)

セキュアなものでなければnetlify.tomlに以下のように追記することで、ローカルでも環境変数を使用できます。

netlify.toml
[build.environment]
  KEY="hogefuga"

:warning: tomlファイルには数値を書くことができないので、値を””で囲むことを忘れずに。

セキュアな情報を扱いたい場合は.envファイルを使用します。
まずはdotenv-webpackをインストールして

npm i dotenv-webpack

netlify-lambdaが使用しているwebpackの設定を上書きするためにwebpack.functions.jsファイルを作成します。
中身は以下の通り。

webpack.functions.js
const Dotenv = require('dotenv-webpack')

module.exports = {
  plugins: [new Dotenv()],
}

あとは.envファイルを作成して中にキーと値の情報を書いて

.env
KEY=value

devコマンドに--configオプションを追加してwebpackに追加のモジュールを読み込ませるようにします。

package.json
"dev": "netlify-lambda serve resources/api --config ./webpack.functions.js",

これでローカルでも環境変数が使えるようになったはずです。
:warning: .envファイルをignore設定するのを忘れないように。

axiosのインストール

npm i axios

axiosを追加。

あとはJSファイルの中でrequireしてあげれば使えます。

const axios = require('axios');

:warning: babelを通してないのでimportは使えません。

環境変数とaxiosを組み合わせたサンプルとしては以下のような感じになります。

const axios = require('axios');

exports.handler = async () => {
  const URL = `https://api.com?key=${process.env.API_KEY}`;
  return axios.get(URL)
    .then(({ data: data }) => ({
      statusCode: 200,
      body: JSON.stringify(data)
    }))
    .catch(e => ({
      statusCode: 400,
      body: e
    }));
};

node-fetchは駄目っぽい

axiosの代わりにnode-fetchを使ってみようとしたところ、ローカルでは動くもののNetlifyでデプロイ時にコケてしまい駄目でした。(netlify-lambdaを使っていない状態なら大丈夫だったので、コンパイル時に何か問題が起こってる?)

色々調べ回っていた時にどこかのIssueで、node-fetchじゃなくてaxiosの使用を推奨しています。みたいな事を書いているのを見かけたのでそういう事なのかなと。

おしまい

ざっくりとした説明でしたが以上になります。
Netlifyをお使いの方はぜひお試しください。

54
Help us understand the problem. What is going on with this article?
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
Sr_Bangs

Comments

No comments
Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account Login
54
Help us understand the problem. What is going on with this article?