ファンクションの書き方
このページではCraft Functionsのコードの書き方について、全てのタイプで共通のルールを説明します。タイプ特有のルールについては、それぞれ次のページを参照してください。
- HTTPタイプ: HTTPタイプのファンクションを作成する
- イベント駆動タイプ: イベント駆動タイプのファンクションを作成する
Craft Functions の記法
Craft Functionsのコードの記法を説明します。
Craft Functions のランタイム
Craft FunctionsはNode.jsの実行環境上で動作します。サポート状況は ファンクションのランタイムとサポート期限 をご確認ください。
Craft Functions のエントリポイント
Craft Functionsでは、次のように定義した関数がエントリポイントとなります。
export default async function (data, { MODULES }) { // この関数内に処理を記述する // Async Functionとして定義しているので、awaitで呼び出す処理が必要}
この関数は次の要件を満たします。
- 関数名は指定しません。
export default
として定義します。 - 引数は
data
とMODULES
要素を持つオブジェクト ({ MODULES }
) の2つです。 - 基本的にはasync functionとして定義します。
入力 (data)
Craft Functionsは外部からの入力として、引数 data
の値を受け取ります。dataの型はファンクションの種類によって異なります。
- HTTPタイプ: オブジェクト
req
,res
を値に持つobject型です。 - イベント駆動タイプ: object型もしくはstring型です。
それぞれの詳細は HTTPタイプのファンクションを作成する および イベント駆動タイプのファンクションを作成する を参照してください。
モジュールの利用
Craft Functionsでは次の方法でモジュールが利用できます。
MODULES
引数の要素として取り出して利用する- npm modulesをコード内でimportする
- Node.jsの標準モジュールを利用する
これらのモジュールの使用方法を説明します。
MODULES
の利用
Craft Functionsの独自モジュールは MODULES
引数の要素として取り出せます。
const { initLogger, secret } = MODULESconst logger = initLogger({ logLevel: 'DEBUG' });
logger.log('infomational log');const { KARTE_SECRET_KEY: secretValue } = await secret.get({ keys: [ 'KARTE_SECRET_KEY' ] });#
MODULES
で利用できるモジュールは Craft Functions で利用できるモジュール で説明します。
npm modules を import する
npm modulesを利用する際にはファンクション編集画面の modules で利用したいモジュールとバージョンをJSON形式で指定します。
例えば、Google Cloud Pub/SubのNode.jsライブラリを利用する場合は modules で次のように指定します。
{ "@google-cloud/pubsub": "3.4.1"}
複数のnpm modulesを利用する場合は、 modules で利用するmoduleをJSON形式で列挙します。
{ "@google-cloud/pubsub": "3.4.1", "@google-cloud/bigquery": "6.1.0"}
npm modulesをCraft Functions内で利用する際は、Craft Functionsのコード内で import
文を使い呼び出します。
import { PubSub } from "@google-cloud/pubsub";
export default async function (data, { MODULES }) { // clientConfigの詳細な設定方法は省略 const clientConfig = { projectId: "hogehoge", credentials: { client_email: "fugafuga", private_key: "foobar", }, }; const topic = new PubSub(clientConfig).topic("topic-name");
// 略}
npm modules の制限
- 現在Craftで利用できるnpm modulesについては、 利用できる npm modules をご参照ください。
- 各moduleの任意のバージョンが指定できます。ただし、チルダ
~
キャレット^
によるバージョン指定はできないのでご注意ください。- 存在しないバージョンを指定するとファンクションの作成に失敗します。
Node.js の標準モジュールを利用する
Node.jsの標準モジュールとして crypto
が利用できます。利用する場合は、Craft Functionsのコードでimportします。
import { randomUUID } from "crypto";
export default async function (data, { MODULES }) { const { initLogger } = MODULES; const logger = initLogger({ logLevel: "DEBUG" }); logger.log(randomUUID());}
関数の同期/非同期
Craft Functionsの大半のユースケースでは、外部システムへのアクセスが発生します。JavaScriptでは特に理由がない限り外部へのリクエストは非同期処理で実現するため、多くの場合、Craft FunctionsはAsync Functionとして定義します。
入力を単純にログとして出力するだけの場合、非同期処理は必要ありません。このような場合、Craft Functionsは同期関数として定義できます。
export default function (data, { MODULES }) { const { initLogger } = MODULES; const logger = initLogger({ logLevel: "DEBUG" }); // for debbuging. logger.log(data);}
Craft Functions の性質
Craft Functionsでプログラムを作成する際に考慮すべき性質について説明します。なお、ファンクションの種類によって特有の性質・ルールについては以下をご確認ください。
Craft Functions は実行環境を使い回すことがある
Craft Functionsは実行環境を使い回すことがあります。同一環境上で実行する場合、Craft Functionsは関数外で定義した値を再計算せず、前回実行時と同じ値を使います。したがって、関数定義の外側で処理時間のかかる処理を行うことで、ファンクションを複数実行した際の平均処理時間を短縮できます。
一方で、実行ごとに異なる値を使う必要がある場合はキャッシュによって意図しない動作を引き起こすので注意してください。次のコードは uuid
にランダムなUUIDを設定していますが、同一実行環境では上記の挙動により同じ値を返却してしまいます。
import crypto from "crypto";
// 関数の外で uuid を定義const uuid = crypto.randomUUID();
export default function (data, { MODULES }) { // 同一実行環境でuuidはキャッシュされるため、短時間に複数回実行すると同じ値が返ることがある return uuid;}
Craft Functions は非同期処理を自動的に完了しない
非同期な処理を行う際は、 必ず関数内で非同期処理の完了を待ってください。 待機しない場合、Craft Functions実行時に次のような不具合が発生し得ます。
- ファンクションの実行が完了しない、もしくは完了するまでに著しく時間がかかる
- 次回の実行時に未完了の処理を再開するなど、意図しない挙動となる
- エラー発生時にログが記録されない
例えば次のようにPromiseチェーンで非同期処理を実装した場合、ファンクションはPromiseチェーン内の非同期処理の完了を待たずに終了することがあります。加えて、次回の実行時に未完了の処理を再開して意図しない挙動をすることがあります。
export default function (data, { MODULES }) { const { initLogger } = MODULES; const logger = initLogger({ logLevel: "DEBUG" });
// Craft Functionsでは次のfetch APIの呼び出し方は非推奨 // (data) => logger.log(data) が完了するまで待機しなければならない fetch("http://example.com/movies.json") .then((response) => response.json()) .then((data) => logger.log(data));}
この場合、Promiseチェーンで書かれた処理を await
で待機することで同期的な処理にできます。
export default function (data, { MODULES }) { const { initLogger } = MODULES; const logger = initLogger({ logLevel: 'DEBUG' });
// await で処理の完了を待つ await fetch("http://example.com/movies.json") .then((response) => response.json()) .then((data) => logger.log(data));}
また、一般的にPromiseチェーンはasync/awaitを使った処理に書き換えられます。非同期処理を実装する際は、await式を適切に利用してください。
export default function (data, { MODULES }) { const { initLogger } = MODULES; const logger = initLogger({ logLevel: 'DEBUG' });
// async/await を使って書き換える const response = await fetch("http://example.com/movies.json"); const resData = await response.json(); logger.log(resData);}
Craft Functions の制約
Craft Functionsには次の制約があります。
- Craft Functions実行時に渡せる入力データの最大サイズは、10KBです。
- 入力データのサイズは、UTF-8エンコードのJSON文字列として計算します。
- サイズ上限を緩和するプランも用意しておりますので、お問い合わせください。
- Craft Functionsの1実行あたりの起動時間の上限は10秒です。
- Craft FunctionsではNode.jsでサポートされているglobal objectの利用を一部制限しています(
require
やprocess
など)。 - デフォルトでCraft Functionsの実行環境のPublic IPアドレスは不定です。Public IPアドレスを固定する場合は固定IPアドレスオプションをご利用ください。
Fetch API の利用
Craft Functions上で外部のリソースにアクセスする場合は、Node.jsの fetch()
を利用します。
Global objects | Node.js v20.12.2 Documentation
例: JSON データを取得する
OpenWeatherMap APIを呼び出して緯度経度から天気情報データをjson形式で取り出します。
// 緯度(lat) 経度(lon) をもとに OpenWeatherMap から天気情報を取得するconst api_key = "API_KEY"; // OpenWeatherMapのAPIキーを指定する。const lat = 35.6696559; // 北緯 35.6696559 度const lon = 139.7639876; // 統計 139.7639876 度const ret = await fetch( `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${api_key}`);const weather_forecast = await ret.json();
例: CSV ファイルを取得する
内閣府が公開している祝日一覧を取得する例です。
// 内閣府のWebサイトにある国民の祝日一覧CSVを取得するconst HOLIDAYS_LIST_URL = "https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv";const res = await fetch(HOLIDAYS_LIST_URL, { method: "GET", headers: { Accept: "text/csv", },});
// 必要に応じてエラー処理を行うif (res.status !== 200) { logger.error(res); throw new Error("cannot get holidays data.");}
// 祝日一覧のCSVから日時情報のみ取得するconst ret = (await res.text()) .split("\\n") .slice(1) .map((line) => line.split(",")[0]);
KARTE のサーバーサイド API (API v2) の利用
Craft FunctionsではKARTEのサーバーサイドAPI (API v2) が利用できます。利用の際にはmodulesで api
を指定し、API リファレンスのNodeの実行例に従ってコードを記述します。
例: Send event to KARTE API (/v2/track/event/write) を実行する
modulesでapiを指定します。
{ "api": "5.0.8"}
コードでAPIを呼び出します。以下はイベント駆動タイプのファンクションとしての実行例です。
api()
の引数にはAPIリファレンスのNodeのリクエスト例(下図)に基づいて設定します。- 参考:Send event to KARTE
import api from "api";
export default async function (data, { MODULES }) { const { initLogger } = MODULES; const logger = initLogger({ logLevel: 'DEBUG' });
// 実際の引数は APIリファレンスNode Example を参照 const insight = api("@dev-karte/vx.y#zzzzzzzzzzzzz"); insight.auth("token_value"); // 実運用の際は、Secret Managerに設定したアクセストークンを利用します。
const res_data = await insight.postV2TrackEventWrite({ keys: { user_id: "test01" }, event: { values: { key1: "value1", key2: "value2" }, event_name: "test_event", }, });
logger.log(res_data);}