Azure Functions の JavaScript 開発者向けガイド

このガイドでは、JavaScript を使用した Azure Functions の開発を成功させるために役立つ詳細情報について説明します。

Express.js、Node.js、または JavaScript の開発者が、Azure Functions を初めて使用する場合は、まず次のいずれかの記事を読むことをお勧めします。

作業の開始 概念 ガイド付き学習

JavaScript 関数の基本

JavaScript (Node.js) 関数はエクスポートされた function であり、トリガーされると実行します (トリガーは function.json で構成します)。 各関数に渡される最初の引数は context オブジェクトで、バインディング データの送受信、ログ記録、ランタイムとの通信に使用されます。

フォルダー構造

JavaScript プロジェクトでは、次のようなフォルダー構造が必要です。 この既定値は変更可能です。 詳しくは、後の scriptFile に関するセクションをご覧ください。

FunctionsProject
 | - MyFirstFunction
 | | - index.js
 | | - function.json
 | - MySecondFunction
 | | - index.js
 | | - function.json
 | - SharedCode
 | | - myFirstHelperFunction.js
 | | - mySecondHelperFunction.js
 | - node_modules
 | - host.json
 | - package.json
 | - extensions.csproj

プロジェクトのルートには、関数アプリの構成に使用できる共有 host.json ファイルがあります。 各関数には、独自のコード ファイル (.js) とバインド構成ファイル (function.json) が含まれるフォルダーがあります。 function.json の親ディレクトリの名前は常に関数の名前です。

Functions ランタイムのバージョン 2.x に必要なバインディング拡張機能は extensions.csproj ファイル内に定義されており、実際のライブラリ ファイルは bin フォルダーにあります。 ローカルで開発する場合は、バインド拡張機能を登録する必要があります。 Azure portal 上で関数を開発するときに、この登録が実行されます。

関数のエクスポート

module.exports (または exports) を使用して、JavaScript 関数をエクスポートする必要があります。 エクスポートする関数は、トリガーされたときに実行される JavaScript 関数である必要があります。

既定では、Functions ランタイムは index.js で関数を検索します。index.js は、対応する function.json と同じ親ディレクトリを共有します。 既定では、エクスポートされる関数は、そのファイルからの唯一のエクスポートである必要があります (run または index という名前のエクスポート)。 関数のファイルの場所とエクスポートの名前を構成する方法については、後の「関数のエントリ ポイントを構成する」をご覧ください。

エクスポートされる関数には、実行時に多数の引数が渡されます。 受け取る最初の引数は常に、context オブジェクトです。 関数が同期の場合 (Promise を返しません)、正しく使用するためには context.done の呼び出しが必要なので、context オブジェクトを渡す必要があります。

// You should include context, other arguments are optional
module.exports = function(context, myTrigger, myInput, myOtherInput) {
    // function logic goes here :)
    context.done();
};

async function をエクスポートする

async function 宣言またはバージョン 2.x の Functions ランタイムでプレーンな JavaScript の Promise を使用するときは、context.done コールバックを呼び出して関数が完了したことを明示的に通知する必要はありません。 エクスポートされた async function/Promise が完了すると、関数は完了します。 バージョン 1.x ランタイムを対象とする関数では、引き続きコードの実行が完了した際に context.done を呼び出す必要があります。

次の例で示すのは、トリガーされてすぐに実行が完了したことを記録する簡単な関数です。

module.exports = async function (context) {
    context.log('JavaScript trigger function processed a request.');
};

async function をエクスポートするときは、return の値を取得するための出力バインドを構成することもできます。 これは、出力バインドが 1 つしかない場合に推奨されます。

return を使用して出力を割り当てるには、function.jsonname プロパティを $return に変更します。

{
  "type": "http",
  "direction": "out",
  "name": "$return"
}

この場合、関数は次の例のようになります。

module.exports = async function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');
    // You can call and await an async method here
    return {
        body: "Hello, world!"
    };
}

バインド

JavaScript では、バインドが構成され、関数の function.json で定義されます。 関数は、さまざまな方法でバインドを操作します。

入力

Azure Functions では、入力は、トリガー入力と追加入力という 2 つのカテゴリに分けられます。 関数は、トリガーと他の入力バインド (direction === "in" のバインド) を 3 つの方法で読み取ることができます。

  • [推奨] 関数に渡されるパラメーターを使用します。 それらは、function.json に定義されている順序で関数に渡されます。 function.json で定義されている name プロパティは、パラメーターの名前と一致する方が望ましいですが、必ずしもそうする必要はありません。

    module.exports = async function(context, myTrigger, myInput, myOtherInput) { ... };
    
  • context.bindings オブジェクトのメンバーを使用します。 各メンバーの名前は、function.json で定義されている name プロパティによって決まります。

    module.exports = async function(context) { 
        context.log("This is myTrigger: " + context.bindings.myTrigger);
        context.log("This is myInput: " + context.bindings.myInput);
        context.log("This is myOtherInput: " + context.bindings.myOtherInput);
    };
    
  • JavaScript の arguments オブジェクトの入力を使用します。 これは、基本的にパラメーターとして入力を渡すのと同じですが、動的に入力を処理することができます。

    module.exports = async function(context) { 
        context.log("This is myTrigger: " + arguments[1]);
        context.log("This is myInput: " + arguments[2]);
        context.log("This is myOtherInput: " + arguments[3]);
    };
    

出力

関数は、さまざまな方法で出力 (direction === "out" のバインド) に書き込むことができます。 どの場合も、function.json で定義されているバインドの name プロパティは、関数に書き込むオブジェクトのメンバーの名前に対応しています。

次の方法のいずれかで (これらの方法を組み合わせることはできません)、出力バインドにデータを割り当てることができます。

  • [出力が複数の場合に推奨] オブジェクトを返します。 非同期関数または Promise を返す関数を使用している場合は、割り当てられた出力データを含むオブジェクトを返すことができます。 次の例の出力バインドは、function.json で "httpResponse" および "queueOutput" という名前が付けられています。

    module.exports = async function(context) {
        let retMsg = 'Hello, world!';
        return {
            httpResponse: {
                body: retMsg
            },
            queueOutput: retMsg
        };
    };
    

    同期関数を使用している場合、context.done を使用してこのオブジェクトを返すことができます (例を参照)。

  • [出力が 1 つの場合に推奨] 直接値を返し $return バインド名を使用します。 これは、関数を返す非同期/Promise でのみ機能します。 「async function をエクスポートする」の例を参照してください。

  • context.bindings に値を割り当てます。 context.bindings に直接値を割り当てることができます。

    module.exports = async function(context) {
        let retMsg = 'Hello, world!';
        context.bindings.httpResponse = {
            body: retMsg
        };
        context.bindings.queueOutput = retMsg;
        return;
    };
    

バインドのデータ型

入力バインドのデータ型を定義するには、バインド定義の dataType プロパティを使用します。 たとえば、バイナリ形式で HTTP 要求のコンテンツを読み取るには、binary 型を使用します。

{
    "type": "httpTrigger",
    "name": "req",
    "direction": "in",
    "dataType": "binary"
}

dataType のオプションは、binarystreamstring です。

context オブジェクト

ランタイムは context オブジェクトを使用して、関数とランタイムとの間でデータを受け渡します。 バインドからのデータの読み取りと設定や、ログへの書き込みのために使用される context オブジェクトは常に、関数に渡される最初のパラメーターです。

同期コードを備えた関数の場合は、context オブジェクトに、その関数が処理を完了したときに呼び出す done コールバックが含まれています。 非同期コードを記述する場合は、done を明示的に呼び出す必要はありません。done コールバックは暗黙的に呼び出されます。

module.exports = (context) => {

    // function logic goes here

    context.log("The function has executed.");

    context.done();
};

関数に渡されるコンテキストでは、次のプロパティを持つオブジェクトである executionContext プロパティが公開されます。

プロパティ名 Type 説明
invocationId String 特定の関数呼び出しのための一意識別子を提供します。
functionName String 実行中の関数の名前を提供します。
functionDirectory String 関数のアプリ ディレクトリを提供します。

次の例は、invocationId を返す方法を示しています。

module.exports = (context, req) => {
    context.res = {
        body: context.executionContext.invocationId
    };
    context.done();
};

context.bindings プロパティ

context.bindings

バインド データの読み取りまたは割り当てに使用される名前付きオブジェクトを返します。 入力およびトリガー バインド データには、context.bindings のプロパティを読み取ることでアクセスできます。 出力バインド データは、context.bindings にデータを追加することで割り当てることができます。

たとえば、function.json で次のようなバインド定義を使用すると、context.bindings.myInput からキューの内容にアクセスし、context.bindings.myOutput を使用して出力をキューに割り当てることができます。

{
    "type":"queue",
    "direction":"in",
    "name":"myInput"
    ...
},
{
    "type":"queue",
    "direction":"out",
    "name":"myOutput"
    ...
}
// myInput contains the input data, which may have properties such as "name"
var author = context.bindings.myInput.name;
// Similarly, you can set your output data
context.bindings.myOutput = { 
        some_text: 'hello world', 
        a_number: 1 };

context.binding オブジェクトの代わりに context.done メソッドを使用して、出力バインド データを定義できます (下記参照)。

context.bindingData プロパティ

context.bindingData

トリガーのメタデータと関数呼び出しデータを含む名前付きオブジェクトを返します (invocationIdsys.methodNamesys.utcNowsys.randGuid)。 トリガーのメタデータの例については、こちらのイベント ハブの例をご覧ください。

context.done メソッド

context.done メソッドは、同期メソッドによって使用されます。

同期実行 非同期実行
(Node 8+、Functions ランタイム 2+)
必須: 関数が完了したとランタイムに通知する context.done([err],[propertyBag])。 これがない場合、実行はタイムアウトします。
context.done メソッドを使用すると、ランタイムに対するユーザー定義のエラーと、出力バインド データを含む JSON オブジェクトの両方を、戻すことができます。 context.done に渡されるプロパティは、context.bindings オブジェクトで設定されているすべてのものを上書きします。
不要: context.done - 暗黙的に呼び出されます。
// Synchronous code only
// Even though we set myOutput to have:
//  -> text: 'hello world', number: 123
context.bindings.myOutput = { text: 'hello world', number: 123 };
// If we pass an object to the done function...
context.done(null, { myOutput: { text: 'hello there, world', noNumber: true }});
// the done method overwrites the myOutput binding to be: 
//  -> text: 'hello there, world', noNumber: true

context.log メソッド

context.log(message)

既定のトレース レベルでストリーミング関数ログに書き込むことができます。他のログ レベルも使用できます。 トレース ログの詳細については、次のセクションを参照してください。

トレース出力をログに書き込む

Functions で、context.log メソッドを使用してトレース出力をログとコンソールに書き込みます。 context.log() を呼び出すと、既定のトレース レベルである、"情報" トレース レベルでログにメッセージが書き込まれます。 Functions は Azure Application Insights と統合されているため、関数アプリのログをより適切にキャプチャすることができます。 Azure Monitor の一部である Application Insights では、アプリケーション テレメトリとトレース出力の両方を収集、視覚的に表示、分析するための機能を使用できます。 詳細については、「Azure Functions の監視」を参照してください。

次の例では、情報トレース レベルで呼び出し ID を含む、ログを書き込んでいます。

context.log("Something has happened. " + context.invocationId); 

すべての context.log メソッドは、Node.js の util.format メソッドでサポートされているのと同じパラメーター形式をサポートしています。 既定のトレース レベルを使用して関数ログに書き込む次のようなコードについて考えます。

context.log('Node.js HTTP trigger function processed a request. RequestUri=' + req.originalUrl);
context.log('Request Headers = ' + JSON.stringify(req.headers));

同じコードを次の形式で記述することもできます。

context.log('Node.js HTTP trigger function processed a request. RequestUri=%s', req.originalUrl);
context.log('Request Headers = ', JSON.stringify(req.headers));

注意

トレース出力の書き込みには、console.log を使用しないでください。 console.log からの出力は関数アプリ レベルでキャプチャされるため、特定の関数呼び出しには関連付けられておらず、特定の関数のログには表示されません。 また、Functions ランタイムのバージョン 1.x では、console.log を使用したコンソールへの書き込みはサポートされていません。

トレース レベル

既定のレベルに加えて、特定のトレース レベルで関数のログを書き込むことができる次の追加のログ記録メソッドがあります。

メソッド 説明
error(message) ログにエラーレベルのイベントを書き込みます。
warn(message) 警告レベルのイベントをログに書き込みます。
info(message) 情報レベルのログ、またはそれ以下に書き込みます。
verbose(message) 詳細なレベルのログに書き込みます。

次の例では、情報レベルではなく、警告トレース レベルで同じログを書き込みます。

context.log.warn("Something has happened. " + context.invocationId); 

エラー は最高のトレース レベルであるため、ログ記録が有効になっている限り、このトレースはすべてのトレース レベルで出力に書き込まれます。

ログ記録のトレース レベルを構成する

Fuctions を使用して、ログまたはコンソールに書き込むためのしきい値のトレース レベルを定義できます。 具体的なしきい値の設定は、使用している Functions ランタイムのバージョンによって異なります。

ログに書き込まれるトレースのしきい値を設定するには、host.json ファイルの logging.logLevel プロパティを使用します。 この JSON オブジェクトを使用すると、関数アプリのすべての関数に既定のしきい値を定義できます。また、個々の関数に対して特定のしきい値を定義することもできます。 詳しくは、Azure Functions で監視を構成する方法に関するページを参照してください。

カスタム テレメトリをログに記録する

既定では、出力は Functions によってトレースとして Application Insights に書き込まれます。 より細かく制御するには、代わりに Application Insights Node.js SDK を使用して、Application Insights インスタンスにカスタム テレメトリ データを送信することもできます。

const appInsights = require("applicationinsights");
appInsights.setup();
const client = appInsights.defaultClient;

module.exports = function (context, req) {
    context.log('JavaScript HTTP trigger function processed a request.');

    // Use this with 'tagOverrides' to correlate custom telemetry to the parent function invocation.
    var operationIdOverride = {"ai.operation.id":context.traceContext.traceparent};

    client.trackEvent({name: "my custom event", tagOverrides:operationIdOverride, properties: {customProperty2: "custom property value"}});
    client.trackException({exception: new Error("handled exceptions can be logged with this method"), tagOverrides:operationIdOverride});
    client.trackMetric({name: "custom metric", value: 3, tagOverrides:operationIdOverride});
    client.trackTrace({message: "trace message", tagOverrides:operationIdOverride});
    client.trackDependency({target:"http://dbname", name:"select customers proc", data:"SELECT * FROM Customers", duration:231, resultCode:0, success: true, dependencyTypeName: "ZSQL", tagOverrides:operationIdOverride});
    client.trackRequest({name:"GET /customers", url:"http://myserver/customers", duration:309, resultCode:200, success:true, tagOverrides:operationIdOverride});

    context.done();
};

tagOverrides パラメーターにより、関数の呼び出し ID に operation_Id を設定します。 この設定により、特定の関数呼び出しについての自動生成されたテレメトリとカスタム テレメトリをすべて関連付けることができます。

HTTP トリガーとバインディング

HTTP、webhook トリガー、および HTTP 出力バインディングでは、要求オブジェクトと応答オブジェクトを使用して HTTP メッセージングを表します。

要求オブジェクト

context.req (要求) オブジェクトには、次のプロパティがあります。

プロパティ 説明
body 要求の本文を格納するオブジェクト。
headers 要求ヘッダーを格納するオブジェクト。
method 要求の HTTP メソッド。
originalUrl 要求の URL。
params 要求のルーティング パラメーターを格納するオブジェクト。
query クエリ パラメーターを格納するオブジェクト。
rawBody 文字列としてのメッセージの本文。

応答オブジェクト

context.res (応答) オブジェクトには、次のプロパティがあります。

プロパティ 説明
body 応答の本文を格納するオブジェクト。
headers 応答ヘッダーを格納するオブジェクト。
isRaw 応答の書式設定をスキップすることを示します。
status 応答の HTTP 状態コード。
cookies 応答に設定される HTTP Cookie オブジェクトの配列。 HTTP Cookie オブジェクトには、namevalue、およびその他の Cookie プロパティ (maxAgesameSite など) があります。

要求と応答へのアクセス

HTTP トリガーを使用する場合、HTTP 要求オブジェクトと応答オブジェクトにアクセスする方法がいくつかあります。

  • context オブジェクトの req プロパティと res プロパティから。 この方法で、完全な context.bindings.name パターンを使用する代わりに、従来のパターンを使用して context オブジェクトから HTTP データにアクセスできます。 次の例では、contextreq オブジェクトと res オブジェクトにアクセスする方法を示します。

    // You can access your HTTP request off the context ...
    if(context.req.body.emoji === ':pizza:') context.log('Yay!');
    // and also set your HTTP response
    context.res = { status: 202, body: 'You successfully ordered more coffee!' }; 
    
  • 名前付きの入力バインディングと出力バインディングから。 この方法で、HTTP トリガーとバインディングは他のバインディングと同じように動作します。 次の例では、名前付き response バインディングを使用して、応答オブジェクトを設定します。

    {
        "type": "http",
        "direction": "out",
        "name": "response"
    }
    
    context.bindings.response = { status: 201, body: "Insert succeeded." };
    
  • [応答のみ] context.res.send(body?: any) の呼び出し。 HTTP の応答は、入力 body を応答本文として使用して作成されます。 context.done() は暗黙的に呼び出されます。

  • [応答のみ] context.done() の呼び出し。 これは、context.done() メソッドに渡される応答を返す特殊な種類の HTTP バインディングです。 次の HTTP 出力バインディングで、$return 出力パラメーターを定義します。

    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
    
     // Define a valid response object.
    res = { status: 201, body: "Insert succeeded." };
    context.done(null, res);   
    

要求と応答のキーは小文字であることに注意してください。

スケーリングと同時性

既定では、Azure Functions は、アプリケーションの負荷を自動的に監視し、必要に応じて node.js 用の追加のホストインスタンスを作成します。 関数は、さまざまなトリガー型の組み込み(ユーザー設定不可)しきい値を使用して、メッセージの経過時間や QueueTrigger のキューサイズなど、インスタンスを追加するタイミングを決定します。 詳細については、「従量課金プランと Premium プランのしくみ」をご覧ください。

ほとんどの Node.js アプリケーションでは、このスケーリング動作で十分です。 CPUにバインドされたアプリケーションの場合、複数の言語ワーカープロセスを使用して、パフォーマンスをさらに向上させることができます。

既定では、すべての Functions ホスト インスタンスに 1 つの言語ワーカー プロセスがあります。 FUNCTIONS_WORKER_PROCESS_COUNT アプリケーション設定を使用して、ホストごとのワーカー プロセスの数を増やすことができます (最大 10)。 次に、Azure Functions は、これらのワーカー間で同時関数呼び出しを均等に分散しようとします。

FUNCTIONS_WORKER_PROCESS_COUNT は、要求に応じてアプリケーションをスケールアウトするときに、関数作成する各ホストに適用されます。

Node バージョン

次の表は、Functions ランタイムの各メジャー バージョンに対して現在サポートされている Node.js バージョンをオペレーティング システムごとに示しています。

Functions バージョン Node バージョン (Windows) Node バージョン (Linux)
3.x (推奨) ~14 (推奨)
~12
~10
node|14 (推奨)
node|12
node|10
2.x ~12
~10
~8
node|10
node|8
1.x 6.11.2 (ランタイムによりロック) 該当なし

ランタイムが使用している現在のバージョンを確認するには、任意の関数から process.version をログに記録します。

Node のバージョンを設定する

Windows 関数アプリの場合は、WEBSITE_NODE_DEFAULT_VERSION アプリ設定をサポートされている LTS バージョン (~14 など) に設定して、Azure のバージョンをターゲットにします。

Linux 関数アプリの場合は、次の Azure CLI コマンドを実行して、Node のバージョンを更新します。

az functionapp config set --linux-fx-version "node|14" --name "<MY_APP_NAME>" --resource-group "<MY_RESOURCE_GROUP_NAME>"

Azure Functions のランタイム サポート ポリシーの詳細については、こちらの記事を参照してください。

依存関係の管理

JavaScript コードでコミュニティ ライブラリを使用するには、次の例で示すように、Azure 内の関数アプリにすべての依存関係がインストールされている必要があります。

// Import the underscore.js library
var _ = require('underscore');
var version = process.version; // version === 'v6.5.0'

module.exports = function(context) {
    // Using our imported underscore.js library
    var matched_names = _
        .where(context.bindings.myInput.names, {first: 'Carla'});

注意

関数アプリのルートに package.json ファイルを定義する必要があります。 このファイルを定義することによって、アプリのすべての関数を同じキャッシュされたパッケージで共有し、最高クラスのパフォーマンスを得ることができます。 バージョンの競合がある場合は、個別の関数のフォルダーに package.json ファイルを追加することで競合を解決できます。

ソース管理から関数アプリをデプロイする場合、リポジトリ内に存在する package.json ファイルはすべて、デプロイ中にそのフォルダー内の npm install をトリガーします。 ただし、ポータルまたは CLI 経由でデプロイする場合は、パッケージを手動でインストールする必要があります。

関数アプリにパッケージをインストールするには 2 つの方法があります。

依存関係と共に展開する

  1. npm install を実行して、すべての必要なパッケージをローカルにインストールします。

  2. コードを展開し、node_modules フォルダーが展開に含まれることを確認します。

Kudu を使用する

  1. https://<function_app_name>.scm.azurewebsites.net にアクセスします。

  2. [デバッグ コンソール] > [CMD] をクリックします。

  3. D:\home\site\wwwroot に移動し、ページの上半分にある wwwroot フォルダーに package.json ファイルをドラッグします。
    関数アプリにファイルをアップロードする方法は、他にもあります。 詳細については、「関数アプリ ファイルを更新する方法」を参照してください。

  4. package.json ファイルがアップロードされたら、Kudu リモート実行コンソールnpm install コマンドを実行します。
    この操作によって、package.json ファイルに示されているパッケージがダウンロードされ、関数アプリが再起動されます。

環境変数

ローカル環境とクラウド環境の両方で、運用シークレット (接続文字列、キー、エンドポイント) や環境設定 (プロファイル変数など) など、独自の環境変数を関数アプリに追加します。 これらの設定には、関数コードで process.env を使用してアクセスします。

ローカル開発環境

ローカルで実行されている場合は、関数プロジェクトに local.settings.json ファイルが含まれています。このファイルに、Values オブジェクトの環境変数が格納されます。

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "translatorTextEndPoint": "https://api.cognitive.microsofttranslator.com/",
    "translatorTextKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "languageWorkers__node__arguments": "--prof"
  }
}

Azure クラウド環境

Azure で実行されている場合は、関数アプリでアプリケーション設定 (サービス接続文字列など) を設定して使用し、これらの設定を実行時に環境変数として公開できます。

関数アプリの設定は、いくつかの方法で追加、更新、削除できます。

関数アプリの設定に変更を加えるためには、関数アプリを再起動する必要があります。

コードの環境変数にアクセスする

2 つ目と 3 つ目の context.log() の呼び出しで示されているように、process.env を使用して環境変数としてアプリケーション設定にアクセスします。ここで、AzureWebJobsStorageWEBSITE_SITE_NAME の環境変数がログに記録されます。

module.exports = async function (context, myTimer) {

    context.log("AzureWebJobsStorage: " + process.env["AzureWebJobsStorage"]);
    context.log("WEBSITE_SITE_NAME: " + process.env["WEBSITE_SITE_NAME"]);
};

ECMAScript モジュール (プレビュー)

注意

現在、ECMAScript モジュールは Node.js 14 で "試験段階" としてラベル付けされているため、Node.js 14 Azure Functions のプレビュー機能として利用できます。 ECMAScript モジュールへの Node.js 14 のサポートが "安定" するまで、API または動作が変更される可能性があります。

ECMAScript モジュール (ES モジュール) は、Node.js 用の新しい公式標準モジュール システムです。 これまで、この記事のコード サンプルは、CommonJS 構文を使用しています。 Node.js 14 で Azure Functions を実行するときに、ES モジュール構文を使用して関数を記述することを選択できます。

関数で ES モジュールを使用するには、.mjs 拡張子を使用するようにファイル名を変更します。 次の index.mjs ファイルの例は、ES モジュール構文を使用して uuid ライブラリをインポートし、値を返す、HTTP によってトリガーされる関数です。

import { v4 as uuidv4 } from 'uuid';

export default async function (context, req) {
    context.res.body = uuidv4();
};

関数のエントリ ポイントを構成する

function.json のプロパティ scriptFileentryPoint を使用して、エクスポートされた関数の名前と場所を構成できます。 これらのプロパティは、JavaScript がトランスパイルされる場合に重要になることがあります。

scriptFile の使用

既定では、JavaScript 関数は index.js から実行されます。これは、対応する function.json と同じ親ディレクトリを共有するファイルです。

scriptFile を使用すると、次の例のようなフォルダー構造を取得できます。

FunctionApp
 | - host.json
 | - myNodeFunction
 | | - function.json
 | - lib
 | | - sayHello.js
 | - node_modules
 | | - ... packages ...
 | - package.json

myNodeFunction に対する function.json には、エクスポートされた関数を実行するファイルを指し示す scriptFile プロパティが含まれている必要があります。

{
  "scriptFile": "../lib/sayHello.js",
  "bindings": [
    ...
  ]
}

entryPoint の使用

scriptFile (または index.js) では、関数が発見されて実行されるためには、module.exports を使用して関数をエクスポートする必要があります。 既定では、トリガーされたときに実行される関数は、そのファイルからの唯一のエクスポートです (run という名前のエクスポート、または index という名前のエクスポート)。

これは、次の例で示すように、entryPointfunction.json を使用して構成することができます。

{
  "entryPoint": "logFoo",
  "bindings": [
    ...
  ]
}

Functions v2.x では、ユーザー関数内の this パラメーターがサポートされており、関数のコードは次の例のようになります。

class MyObj {
    constructor() {
        this.foo = 1;
    };

    logFoo(context) { 
        context.log("Foo is " + this.foo); 
        context.done(); 
    }
}

const myObj = new MyObj();
module.exports = myObj;

この例では、オブジェクトはエクスポートされていますが、実行間で状態が保持される保証はないことに注意することが重要です。

ローカル デバッグ

Node.js プロセスは、--inspect パラメーターを指定して起動されると、指定されたポートでデバッグ クライアントをリッスンします。 Azure Functions 2.x では、環境変数またはアプリ設定 languageWorkers:node:arguments = <args> を追加することで、コードを実行する Node.js プロセスに渡す引数を指定できます。

ローカルでデバッグするには、local.settings.json ファイルの Values の下に "languageWorkers:node:arguments": "--inspect=5858" を追加し、デバッガーをポート 5858 に接続します。

VS Code を使用してデバッグするときは、プロジェクトの launch.json ファイルの port 値を使用して、--inspect パラメーターが自動的に追加されます。

バージョン 1.x では、設定 languageWorkers:node:arguments は機能しません。 デバッグ ポートは、Azure Functions Core Tools の --nodeDebugPort パラメーターを使用して選択できます。

TypeScript

Functions ランタイムのバージョン 2.x を対象とする場合、Visual Studio Code 用の Azure FunctionsAzure Functions Core Tools の両方によって、TypeScript 関数アプリ プロジェクトをサポートするテンプレートを使用して関数アプリを作成することができます。 テンプレートは、package.json および tsconfig.json プロジェクト ファイルを生成します。これらのプロジェクト ファイルは、これらのツールによって TypeScript コードから JavaScript 関数のトランスパイル、実行、発行を容易にします。

生成された .funcignore ファイルは、プロジェクトが Azure に発行されるときに除外するファイルを指定するために使用します。

TypeScript ファイル (.ts) は、dist 出力ディレクトリ内の JavaScript ファイル (.js) にトランスパイルされます。 TypeScript テンプレートは、function.jsonscriptFile パラメーターを使用して dist フォルダー内の対応する .js ファイルの場所を示します。 出力場所は、tsconfig.json ファイルの outDir パラメーターを使用することで、テンプレートによって設定されます。 この設定またはフォルダーの名前を変更した場合、ランタイムは実行するコードを見つけることができません。

TypeScript プロジェクトからローカルで開発およびデプロイする方法は、開発ツールによって異なります。

Visual Studio Code

Visual Studio Code 用の Azure Functions の拡張機能を使用すると、TypeScript を使用して関数を開発することができます。 Core Tools は Azure Functions の拡張機能の要件です。

Visual Studio Code で TypeScript 関数アプリを作成するには、関数アプリを作成するときに言語として TypeScript を選択します。

F5 を押してアプリをローカルで実行すると、ホスト (func.exe) が初期化される前にトランスパイルが実行されます。

[Deploy to function app... (関数アプリにデプロイする...)] ボタンを使用して関数アプリを Azure にデプロイすると、Azure Functions の拡張機能はまず、TypeScript ソース ファイルから実稼働可能な JavaScript ファイルのビルドを生成します。

Azure Functions Core Tools

Core Tools の使用に関して、TypeScript プロジェクトと JavaScript プロジェクトでは異なる点がいくつかあります。

Create project

Core Tools を使用して TypeScript 関数アプリ プロジェクトを作成するには、関数アプリを作成するときに TypeScript 言語オプションを指定する必要があります。 これは、次の方法のいずれかで実行できます。

  • func init コマンドを実行し、言語スタックとして node を選択してから typescript を選択してください。

  • func init --worker-runtime typescript コマンドを実行します。

ローカルで実行する

Core Tools を使用して関数アプリのコードをローカルで実行するには、func host start ではなく次のコマンドを使用します。

npm install
npm start

npm start コマンドは次のコマンドと同等です。

  • npm run build
  • func extensions install
  • tsc
  • func start

Azure に発行する

func azure functionapp publish コマンドを使用して Azure にデプロイする前に、TypeScript ソース ファイルから JavaScript ファイルの運用対応のビルドを作成します。

Core Tools を使用し、次のコマンドで TypeScript プロジェクトを準備して発行します。

npm run build:production 
func azure functionapp publish <APP_NAME>

このコマンドでは、<APP_NAME> を実際の関数アプリの名前に置き換えます。

JavaScript 関数に関する考慮事項

JavaScript 関数を使用するときは、以下のセクションに記載されている事柄に注意する必要があります。

シングル vCPU App Service プランを選択する

App Service プランを使用する関数アプリを作成するときは、複数の vCPU を持つプランではなく、シングル vCPU プランを選択することをお勧めします。 今日では、関数を使用して、シングル vCPU VM で JavaScript 関数をより効率的に実行できるようになりました。そのため、大規模な VM を使用しても、期待以上にパフォーマンスが向上することはありません。 必要な場合は、シングル vCPU VM インスタンスを追加することで手動でスケールアウトするか、自動スケーリングを有効にすることができます。 詳細については、「手動または自動によるインスタンス数のスケール変更」を参照してください。

コールド スタート

サーバーレス ホスティング モデルで Azure 関数を開発するときは、コールド スタートが現実のものになります。 コールド スタート とは、非アクティブな期間の後で初めて関数アプリが起動するとき、起動に時間がかかることを意味します。 特に、大きな依存関係ツリーを持つ JavaScript 関数の場合は、コールド スタートが重要になる可能性があります。 コールド スタート プロセスをスピードアップするには、可能な場合、パッケージ ファイルとして関数を実行します。 多くの展開方法ではパッケージからの実行モデルが既定で使用されますが、大規模なコールド スタートが発生していて、この方法で実行していない場合は、変更が大きな向上につながる可能性があります。

接続の制限

Azure Functions アプリケーションでサービス固有のクライアントを使用する場合は、関数呼び出しごとに新しいクライアントを作成しないでください。 代わりに、グローバル スコープに 1 つの静的クライアントを作成してください。 詳細については、Azure Functions での接続の管理に関するページを参照してください。

asyncawait を使用する

Azure Functions を JavaScript で記述する場合、asyncawait のキーワードを使用してコードを記述することをお勧めします。 コールバックや Promise の .then および .catch の代わりに asyncawait を使用してコードを記述することにより、2 つの一般的な問題を回避できます。

  • Node.js プロセスをクラッシュさせる、キャッチされない例外のスロー。他の関数の実行に影響する可能性があります。
  • 適切に待機しない非同期呼び出しによって発生する、context.log からのログの欠落などの予期しない動作。

以下の例では、その 2 番目のパラメーターとしてエラーファースト コールバック関数を使用して非同期メソッド fs.readFile が呼び出されます。 このコードは上記の両方の問題の原因となります。 正しいスコープでは明示的にキャッチされない例外によって、プロセス全体がクラッシュします (問題 1)。 コールバック関数のスコープ外で context.done() を呼び出すことは、ファイルが読み取られる前に関数呼び出しが終了する可能性があることを意味します (問題 2)。 この例では、context.done() の呼び出しが早すぎるため、結果として Data from file: で始まるログ エントリが欠落します。

// NOT RECOMMENDED PATTERN
const fs = require('fs');

module.exports = function (context) {
    fs.readFile('./hello.txt', (err, data) => {
        if (err) {
            context.log.error('ERROR', err);
            // BUG #1: This will result in an uncaught exception that crashes the entire process
            throw err;
        }
        context.log(`Data from file: ${data}`);
        // context.done() should be called here
    });
    // BUG #2: Data is not guaranteed to be read before the Azure Function's invocation ends
    context.done();
}

async および await のキーワードを使用すると、これらの両方のエラーを回避しやすくなります。 Node.js のユーティリティ関数 util.promisify を使用して、エラーファースト コールバック スタイルの関数を、待機可能な関数に変更してください。

次の例では、関数の実行中にスローされたハンドルされない例外により、例外を発生させた個々の呼び出しのみが失敗します。 await キーワードは、readFileAsync に続くステップが、readFile の完了後にのみ実行されることを意味しています。 asyncawait を使用することで、context.done() コールバックを呼び出す必要もありません。

// Recommended pattern
const fs = require('fs');
const util = require('util');
const readFileAsync = util.promisify(fs.readFile);

module.exports = async function (context) {
    let data;
    try {
        data = await readFileAsync('./hello.txt');
    } catch (err) {
        context.log.error('ERROR', err);
        // This rethrown exception will be handled by the Functions Runtime and will only fail the individual invocation
        throw err;
    }
    context.log(`Data from file: ${data}`);
}

次のステップ

詳細については、次のリソースを参照してください。