Guide du développeur Node.js sur Azure Functions

Ce guide est une introduction au développement d’Azure Functions à l’aide de JavaScript ou TypeScript. L’article suppose que vous avez déjà lu le Guide de développement Azure Functions.

Important

Le contenu de cet article change en fonction de votre choix du modèle de programmation Node.js dans le sélecteur en haut de cette page. La version que vous choisissez doit correspondre à la version du package npm @azure/functions que vous utilisez dans votre application. Si ce package n’est pas répertorié dans votre package.json, la valeur par défaut est v3. En savoir plus sur les différences entre v3 et v4 dans le guide de migration.

En tant que développeur Node.js, vous pouvez également être intéressé par l’un des articles suivants :

Prise en main Concepts Apprentissage guidé

À propos de l’installation

  • Le modèle de programmation Node.js ne doit pas être confondu avec le runtime Azure Functions :
    • Modèle de programmation : définit la façon dont vous créez votre code et est spécifique à JavaScript et TypeScript.
    • Runtime : définit le comportement sous-jacent de Azure Functions et est partagé entre tous les langages.
  • La version du modèle de programmation est strictement liée à celle du package npm @azure/functions. Cela est versionné indépendamment du runtime. Le runtime et le modèle de programmation utilisent tous les deux le nombre 4 comme dernière version majeure, mais cela est une coïncidence.
  • Vous ne pouvez pas combiner les modèles de programmation v3 et v4 dans la même application de fonction. Dès que vous inscrivez une fonction v4 dans votre application, toutes les fonctions v3 inscrites dans des fichiers function.json sont ignorées.

Versions prises en charge

Le tableau suivant montre chaque version du modèle de programmation Node.js ainsi que ses versions prises en charge du runtime et de l'Node.js Azure Functions.

Version du modèle de programmation Niveau de prise en charge Version du runtime Functions Version de Node.js Description
4.x GA 4.25+ 20.x, 18.x Prend en charge une structure de fichiers flexible et une approche centrée sur le code pour les déclencheurs et les liaisons.
3.x GA 4.x 20.x, 18.x, 16.x, 14.x Nécessite une structure de fichiers spécifique avec vos déclencheurs et liaisons déclarés dans un fichier « function.json »
2.x n/a 3.x 14.x, 12.x, 10.x A atteint la fin de la prise en charge le 13 décembre 2022. Pour plus d’informations, consultez Versions de Functions.
1.x n/a 2.x 10.x, 8.x A atteint la fin de la prise en charge le 13 décembre 2022. Pour plus d’informations, consultez Versions de Functions.

Structure de dossiers

La structure de dossiers requise pour un projet JavaScript est similaire à l’exemple suivant :

<project_root>/
 | - .vscode/
 | - node_modules/
 | - myFirstFunction/
 | | - index.js
 | | - function.json
 | - mySecondFunction/
 | | - index.js
 | | - function.json
 | - .funcignore
 | - host.json
 | - local.settings.json
 | - package.json

Le dossier principal du projet <project_root> peut contenir les fichiers suivants :

  • .vscode/ : (facultatif) contient la configuration Visual Studio Code stockée. Pour plus d’informations, consultez Paramètres Visual Studio Code.
  • myFirstFunction/function.json : contient la configuration du déclencheur, des entrées et des sorties de la fonction. Le nom du répertoire détermine le nom de votre fonction.
  • myFirstFunction/index.js : stocke le code de votre fonction. Pour changer ce chemin de fichier par défaut, consultez Utilisation de scriptFile.
  • .funcignore : (Facultatif) Déclare des fichiers qui ne devraient pas être publiés sur Azure. En règle générale, ce fichier contient .vscode/ pour ignorer le paramètre de votre éditeur, test/ pour ignorer les cas de test et local.settings.json pour empêcher la publication des paramètres de l’application locale.
  • host.json : contient les options de configuration qui affectent toutes les fonctions d’une instance d’application de fonction. Ce fichier est publié sur Azure. Toutes les options ne sont pas prises en charge lors de l’exécution locale. Pour en savoir plus, consultez la section host.json.
  • local.settings.json : utilisé pour stocker les paramètres d’application et les chaînes de connexion lors d’une exécution en local. Ce fichier n’est pas publié sur Azure. Pour en savoir plus, consultez la section local.settings.file.
  • package.json : contient des options de configuration telles qu’une liste de dépendances de package, le point d’entrée principal et des scripts.

La structure de dossiers recommandée d’un projet JavaScript se présente comme l’exemple suivant :

<project_root>/
 | - .vscode/
 | - node_modules/
 | - src/
 | | - functions/
 | | | - myFirstFunction.js
 | | | - mySecondFunction.js
 | - test/
 | | - functions/
 | | | - myFirstFunction.test.js
 | | | - mySecondFunction.test.js
 | - .funcignore
 | - host.json
 | - local.settings.json
 | - package.json

Le dossier principal du projet <project_root> peut contenir les fichiers suivants :

  • .vscode/ : (facultatif) contient la configuration Visual Studio Code stockée. Pour plus d’informations, consultez Paramètres Visual Studio Code.
  • src/functions/ : l’emplacement par défaut pour toutes les fonctions et leurs déclencheurs et liaisons associés.
  • test/ : (Facultatif) Contient les cas de test de votre application de fonction.
  • .funcignore : (Facultatif) Déclare des fichiers qui ne devraient pas être publiés sur Azure. En règle générale, ce fichier contient .vscode/ pour ignorer le paramètre de votre éditeur, test/ pour ignorer les cas de test et local.settings.json pour empêcher la publication des paramètres de l’application locale.
  • host.json : contient les options de configuration qui affectent toutes les fonctions d’une instance d’application de fonction. Ce fichier est publié sur Azure. Toutes les options ne sont pas prises en charge lors de l’exécution locale. Pour en savoir plus, consultez la section host.json.
  • local.settings.json : utilisé pour stocker les paramètres d’application et les chaînes de connexion lors d’une exécution en local. Ce fichier n’est pas publié sur Azure. Pour en savoir plus, consultez la section local.settings.file.
  • package.json : contient des options de configuration telles qu’une liste de dépendances de package, le point d’entrée principal et des scripts.

Inscription d’une fonction

Le modèle v3 inscrit une fonction en se basant sur l’existence de deux fichiers. Tout d’abord, vous avez besoin d’un fichier function.json situé dans un dossier se trouvant un niveau sous la racine de votre application. Ensuite, vous avez besoin d’un fichier JavaScript qui exporte votre fonction. Par défaut, le modèle recherche un fichier index.js se trouvant dans le même dossier que votre fichier function.json. Si vous utilisez TypeScript, vous devez utiliser la propriété scriptFile dans function.json pour pointer vers le fichier JavaScript compilé. Pour personnaliser l’emplacement de fichier ou le nom d’exportation de votre fonction, consultez la configuration du point d’entrée de votre fonction.

La fonction que vous exportez doit toujours être déclarée en tant que async function dans le modèle v3. Vous pouvez exporter une fonction synchrone, mais vous devez ensuite appeler context.done() pour signaler que votre fonction est terminée, ce qui est déprécié et non recommandé.

Un appel context passé à votre fonction en tant que premier argument et vos entrées sont passées en tant qu’arguments restants.

L’exemple suivant est une fonction simple qui enregistre dans un journal qu’elle a été déclenchée et répond avec Hello, world! :

{
  "bindings": [
    {
      "type": "httpTrigger",
      "direction": "in",
      "name": "req",
      "authLevel": "anonymous",
      "methods": [
        "get",
        "post"
      ]
    },
    {
      "type": "http",
      "direction": "out",
      "name": "res"
    }
  ]
}
module.exports = async function (context, request) {
    context.log('Http function was triggered.');
    context.res = { body: 'Hello, world!' };
};

Le modèle de programmation charge vos fonctions en fonction du champ main dans votre package.json. Vous pouvez définir le champ main sur un fichier unique ou plusieurs fichiers à l’aide d’un modèle Glob. Le tableau suivant présente des exemples de valeurs pour le champ main :

Exemple Description
src/index.js Inscrivez des fonctions à partir d’un seul fichier racine.
src/functions/*.js Inscrivez chaque fonction à partir de son propre fichier.
src/{index.js,functions/*.js} Une combinaison dans laquelle vous inscrivez chaque fonction à partir de son propre fichier, mais vous disposez toujours d’un fichier racine pour le code général au niveau de l’application.

Pour inscrire une fonction, vous devez importer l’objet app à partir du module npm @azure/functions et appeler la méthode spécifique à votre type de déclencheur. Le premier argument lors de l’inscription d’une fonction est le nom de la fonction. Le deuxième argument est un objet options spécifiant la configuration de votre déclencheur, de votre gestionnaire et de toutes les autres entrées ou sorties. Dans les cas où la configuration du déclencheur n’est pas nécessaire, vous pouvez passer le gestionnaire directement comme deuxième argument plutôt qu’un objet options.

L’inscription d’une fonction peut être effectuée à partir de n’importe quel fichier de votre projet, à condition que ce fichier soit chargé (directement ou indirectement) en fonction du champ main dans votre fichier package.json. La fonction doit être inscrite dans une étendue globale, car vous ne pouvez pas inscrire les fonctions une fois que les exécutions ont commencé.

L’exemple suivant est une fonction simple qui enregistre dans un journal qu’elle a été déclenchée et répond avec Hello, world! :

const { app } = require('@azure/functions');

app.http('helloWorld1', {
    methods: ['POST', 'GET'],
    handler: async (request, context) => {
        context.log('Http function was triggered.');
        return { body: 'Hello, world!' };
    }
});

Entrées et sorties

Votre fonction doit avoir exactement une entrée primaire appelée déclencheur. Il peut également avoir des entrées et/ou des sorties secondaires. Les entrées et sorties, qui sont également appelées liaisons, sont configurées dans vos fichiers function.json.

Entrées

Les entrées sont des liaisons dont la valeur direction est définie sur in. La principale différence entre un déclencheur et une entrée secondaire est que le type d’un déclencheur se termine par Trigger ; par exemple, le type blobTrigger par rapport au type blob. La plupart des fonctions utilisent uniquement un déclencheur, et peu de types d’entrée secondaires sont pris en charge.

Les entrées sont accessibles de plusieurs façons :

  • [Recommandé] En tant qu’arguments passés à votre fonction : utilisez les arguments dans le même ordre que celui dans lequel ils sont définis dans function.json. La propriété namedéfinie dans function.json n’a pas besoin de correspondre au nom de votre argument, même si cela est recommandé par souci d’organisation.

    module.exports = async function (context, myTrigger, myInput, myOtherInput) { ... };
    
  • En tant que propriétés de context.bindings : utilisez la clé correspondant à la propriété name définie dans function.json.

    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);
    };
    

Sorties

Les sorties sont des liaisons dont la valeur direction est définie sur out et qui peuvent être définies de plusieurs façons :

  • [Recommandé en cas de sortie unique] Retourner la valeur directement : si vous utilisez une fonction asynchrone, vous pouvez retourner la valeur directement. Vous devez remplacer la propriété name de la liaison de sortie par $return dans function.json, comme dans l’exemple suivant :

    {
        "name": "$return",
        "type": "http",
        "direction": "out"
    }
    
    module.exports = async function (context, request) {
        return {
            body: "Hello, world!"
        };
    }
    
  • [Recommandé en cas de sorties multiples] Retourner un objet contenant toutes les sorties : si vous utilisez une fonction asynchrone, vous pouvez retourner un objet avec une propriété correspondant au nom de chaque liaison dans votre fichier function.json. L’exemple suivant utilise des liaisons de sortie nommées « httpResponse » et « queueOutput » :

    {
        "name": "httpResponse",
        "type": "http",
        "direction": "out"
    },
    {
        "name": "queueOutput",
        "type": "queue",
        "direction": "out",
        "queueName": "helloworldqueue",
        "connection": "storage_APPSETTING"
    }
    
    module.exports = async function (context, request) {
        let message = 'Hello, world!';
        return {
            httpResponse: {
                body: message
            },
            queueOutput: message
        };
    };
    
  • Définir des valeurs sur context.bindings : si vous n’utilisez pas une fonction asynchrone ou si vous ne voulez pas utiliser les options précédentes, vous pouvez définir des valeurs directement sur context.bindings, où la clé correspond au nom de la liaison. L’exemple suivant utilise des liaisons de sortie nommées « httpResponse » et « queueOutput » :

    {
        "name": "httpResponse",
        "type": "http",
        "direction": "out"
    },
    {
        "name": "queueOutput",
        "type": "queue",
        "direction": "out",
        "queueName": "helloworldqueue",
        "connection": "storage_APPSETTING"
    }
    
    module.exports = async function (context, request) {
        let message = 'Hello, world!';
        context.bindings.httpResponse = {
            body: message
        };
        context.bindings.queueOutput = message;
    };
    

Type de données de liaisons

Vous pouvez utiliser la propriété dataType sur une liaison d’entrée pour changer le type de votre entrée, mais elle présente certaines limitations :

  • Dans Node.js, seuls string et binary sont pris en charge (stream ne l’est pas)
  • Pour les entrées HTTP, la propriété dataType est ignorée. Utilisez à la place des propriétés sur l’objet request pour obtenir le corps dans le format que vous souhaitez. Pour plus d’informations, consultez Requête HTTP.

Dans l’exemple suivant d’un déclencheur de file d’attente de stockage, le type par défaut de myQueueItem est string (chaîne), mais si vous définissez dataType sur binary, le type devient un Buffer (mémoire tampon) Node.js.

{
    "name": "myQueueItem",
    "type": "queueTrigger",
    "direction": "in",
    "queueName": "helloworldqueue",
    "connection": "storage_APPSETTING",
    "dataType": "binary"
}
const { Buffer } = require('node:buffer');

module.exports = async function (context, myQueueItem) {
    if (typeof myQueueItem === 'string') {
        context.log('myQueueItem is a string');
    } else if (Buffer.isBuffer(myQueueItem)) {
        context.log('myQueueItem is a buffer');
    }
};

Votre fonction doit avoir exactement une entrée primaire appelée déclencheur. Il peut également avoir des entrées secondaires, une sortie primaire appelée sortie de retour et/ou des sorties secondaires. Les entrées et les sorties sont également appelées liaisons en dehors du contexte du modèle de programmation Node.js. Avant la version 4 du modèle, ces liaisons étaient configurées dans des fichiers function.json.

Entrée du déclencheur

Le déclencheur est la seule entrée ou sortie requise. Pour la plupart des types de déclencheurs, vous inscrivez une fonction à l’aide d’une méthode sur l’objet app nommé d’après le type de déclencheur. Vous pouvez spécifier une configuration spécifique au déclencheur directement sur l’argument options. Par exemple, un déclencheur HTTP vous permet de spécifier un itinéraire. Pendant l’exécution, la valeur correspondant à ce déclencheur est transmise en tant que premier argument à votre gestionnaire.

const { app } = require('@azure/functions');

app.http('helloWorld1', {
    route: 'hello/world',
    handler: async (request, context) => {
        ...
    }
});

Sortie de retour

La sortie de retour est facultative et, dans certains cas, configurée par défaut. Par exemple, un déclencheur HTTP inscrit avec app.http est configuré pour retourner automatiquement une sortie de réponse HTTP. Pour la plupart des types de sortie, vous spécifiez la configuration de retour sur l’argument options à l’aide de l’objet output exporté à partir du module @azure/functions. Pendant l’exécution, vous définissez cette sortie en la retournant à partir de votre gestionnaire.

L’exemple suivant utilise un déclencheur de minuteur et une sortie de file d’attente de stockage :

const { app, output } = require('@azure/functions');

app.timer('timerTrigger1', {
    schedule: '0 */5 * * * *',
    return: output.storageQueue({
        connection: 'storage_APPSETTING',
        ...
    }),
    handler: (myTimer, context) => {
        return { hello: 'world' }
    }
});

Entrées et sorties supplémentaires

En plus du déclencheur et du retour, vous pouvez spécifier des entrées ou des sorties supplémentaires dans l’argument options lors de l’inscription d’une fonction. Les objets input et output exportés à partir du module @azure/functions fournissent des méthodes spécifiques au type pour aider à construire la configuration. Pendant l’exécution, vous obtenez ou définissez les valeurs avec context.extraInputs.get ou context.extraOutputs.set, en passant l’objet de configuration d’origine comme premier argument.

L’exemple suivant est une fonction déclenchée par une file d’attente de stockage, avec une entrée d’objet blob de stockage supplémentaire qui est copiée dans une sortie d’objet blob de stockage supplémentaire. Le message de file d’attente doit être le nom d’un fichier et remplace {queueTrigger} comme nom d’objet blob à copier, à l’aide d’une expression de liaison.

const { app, input, output } = require('@azure/functions');

const blobInput = input.storageBlob({
    connection: 'storage_APPSETTING',
    path: 'helloworld/{queueTrigger}',
});

const blobOutput = output.storageBlob({
    connection: 'storage_APPSETTING',
    path: 'helloworld/{queueTrigger}-copy',
});

app.storageQueue('copyBlob1', {
    queueName: 'copyblobqueue',
    connection: 'storage_APPSETTING',
    extraInputs: [blobInput],
    extraOutputs: [blobOutput],
    handler: (queueItem, context) => {
        const blobInputValue = context.extraInputs.get(blobInput);
        context.extraOutputs.set(blobOutput, blobInputValue);
    }
});

Entrées et sorties génériques

Les objets app, trigger, input et output exportés par le module @azure/functions fournissent des méthodes spécifiques au type pour la plupart des types. Pour tous les types qui ne sont pas pris en charge, une méthode generic est fournie pour vous permettre de spécifier manuellement la configuration. La méthode generic peut également être utilisée si vous souhaitez modifier les paramètres par défaut fournis par une méthode spécifique à un type.

L’exemple suivant est une fonction simple déclenchée via HTTP utilisant des méthodes génériques au lieu de méthodes spécifiques au type.

const { app, output, trigger } = require('@azure/functions');

app.generic('helloWorld1', {
    trigger: trigger.generic({
        type: 'httpTrigger',
        methods: ['GET', 'POST']
    }),
    return: output.generic({
        type: 'http'
    }),
    handler: async (request, context) => {
        context.log(`Http function processed request for url "${request.url}"`);

        return { body: `Hello, world!` };
    }
});

Contexte d’appel

Un objet context d’appel, utilisé pour lire des entrées, définir des sorties, écrire dans des journaux et lire diverses métadonnées, est passé à chaque appel de votre fonction. Dans le modèle v3, l’objet de contexte est toujours le premier argument passé à votre gestionnaire.

L’objet context dispose des propriétés suivantes :

Propriété Description
invocationId ID de l’appel de fonction en cours.
executionContext Consultez Contexte d’exécution.
bindings Consultez Liaisons.
bindingData Métadonnées relatives à l’entrée de déclencheur pour cet appel, ce qui exclut la valeur elle-même. Par exemple, un déclencheur de hub d’événements a une propriété enqueuedTimeUtc.
traceContext Le contexte pour le suivi distribué. Pour plus d’informations, consultez Trace Context.
bindingDefinitions Configuration de vos entrées et sorties, comme défini dans function.json.
req Consultez Requête HTTP.
res Consultez Réponse HTTP.

context.executionContext

L’objet context.executionContext dispose des propriétés suivantes :

Propriété Description
invocationId ID de l’appel de fonction en cours.
functionName Nom de la fonction qui est appelée. Le nom du dossier contenant le fichier function.json détermine le nom de la fonction.
functionDirectory Dossier contenant le fichier function.json.
retryContext Consultez retryContext.

context.executionContext.retryContext

L’objet context.executionContext.retryContext dispose des propriétés suivantes :

Propriété Description
retryCount Nombre représentant la nouvelle tentative en cours.
maxRetryCount Nombre maximal de nouvelles tentatives d’une exécution. Une valeur de -1 signifie qu’il faut effectuer ces nouvelles tentatives indéfiniment.
exception Exception ayant provoqué la nouvelle tentative.

context.bindings

L’objet context.bindings est utilisé pour lire des entrées ou définir des sorties. L’exemple suivant est un déclencheur de file d’attente de stockage, qui utilise context.bindings pour copier une entrée d’objet blob de stockage dans une sortie d’objet blob de stockage. Le contenu du message de file d’attente remplace {queueTrigger} comme nom de fichier à copier, à l’aide d’une expression de liaison.

{
    "name": "myQueueItem",
    "type": "queueTrigger",
    "direction": "in",
    "connection": "storage_APPSETTING",
    "queueName": "helloworldqueue"
},
{
    "name": "myInput",
    "type": "blob",
    "direction": "in",
    "connection": "storage_APPSETTING",
    "path": "helloworld/{queueTrigger}"
},
{
    "name": "myOutput",
    "type": "blob",
    "direction": "out",
    "connection": "storage_APPSETTING",
    "path": "helloworld/{queueTrigger}-copy"
}
module.exports = async function (context, myQueueItem) {
    const blobValue = context.bindings.myInput;
    context.bindings.myOutput = blobValue;
};

context.done

La méthode context.done est déconseillée. Avant que les fonctions asynchrones soient prises en charge, vous signaliez que l’exécution de votre fonction était terminée en appelant context.done() :

module.exports = function (context, request) {
    context.log("this pattern is now deprecated");
    context.done();
};

Maintenant, il est recommandé de supprimer l’appel à context.done() et de marquer votre fonction comme asynchrone afin qu’elle retourne une promesse, même si vous n’attendez (await) rien. Dès que l’exécution de votre fonction se termine (en d’autres termes, à la résolution de la promesse retournée), le modèle v3 sait que votre fonction est terminée.

module.exports = async function (context, request) {
    context.log("you don't need context.done or an awaited call")
};

Un objet context d’appel, avec des informations sur votre appel et les méthodes utilisées pour la journalisation, est passé à chaque appel de votre fonction. Dans le modèle v4, l’objet context est généralement le deuxième argument passé à votre gestionnaire.

La classe InvocationContext a les propriétés suivantes:

Propriété Description
invocationId ID de l’appel de fonction en cours.
functionName Nom de la fonction.
extraInputs Utilisé pour obtenir les valeurs d’entrées supplémentaires. Pour plus d’informations, consultez Entrées et sorties supplémentaires.
extraOutputs Utilisé pour définir les valeurs des sorties supplémentaires. Pour plus d’informations, consultez Entrées et sorties supplémentaires.
retryContext Consultez retryContext.
traceContext Le contexte pour le suivi distribué. Pour plus d’informations, consultez Trace Context.
triggerMetadata Métadonnées relatives à l’entrée de déclencheur pour cet appel, ce qui exclut la valeur elle-même. Par exemple, un déclencheur de hub d’événements a une propriété enqueuedTimeUtc.
options Les options utilisées lors de l’inscription de la fonction, une fois qu’elles ont été validées et avec les valeurs par défaut spécifiées explicitement.

Contexte de nouvelle tentative

L’objet retryContext dispose des propriétés suivantes :

Propriété Description
retryCount Nombre représentant la nouvelle tentative en cours.
maxRetryCount Nombre maximal de nouvelles tentatives d’une exécution. Une valeur de -1 signifie qu’il faut effectuer ces nouvelles tentatives indéfiniment.
exception Exception ayant provoqué la nouvelle tentative.

Pour plus d’informations, consultez retry-policies.

Journalisation

Dans Azure Functions, il est recommandé d’utiliser context.log() pour écrire des journaux. Azure Functions s’intègre à Azure Application Insights pour mieux capturer les journaux de vos applications de fonction. Application Insights, qui fait partie de Azure Monitor, fournit des ressources pour la collecte, le rendu visuel, ainsi que l’analyse des journaux des applications et de vos sorties de trace. Pour en savoir plus, consultez Surveiller l’exécution des fonctions Azure.

Notes

Si vous utilisez l’autre méthode Node.js console.log, ces journaux sont suivis au niveau de l’application et ne sont associés à aucune fonction spécifique. Il est vivement recommandé d’utiliser context pour la journalisation plutôt que console pour que tous les journaux soient associés à une fonction spécifique.

L’exemple suivant écrit un journal au niveau « informations » par défaut, notamment l’ID d’appel :

context.log(`Something has happened. Invocation ID: "${context.invocationId}"`);

Niveaux de journalisation

Outre la méthode context.log par défaut, les méthodes suivantes sont disponibles pour vous permettre d’écrire des journaux à des niveaux spécifiques :

Méthode Description
context.log.error() Écrit un événement au niveau de l’erreur dans les journaux.
context.log.warn() Écrit un événement de niveau avertissement dans les journaux.
context.log.info() Écrit un événement de niveau information dans les journaux.
context.log.verbose() Écrit un événement de niveau trace dans les journaux.
Méthode Description
context.trace() Écrit un événement de niveau trace dans les journaux.
context.debug() Écrit un événement de niveau débogage dans les journaux.
context.info() Écrit un événement de niveau information dans les journaux.
context.warn() Écrit un événement de niveau avertissement dans les journaux.
context.error() Écrit un événement au niveau de l’erreur dans les journaux.

Configurer le niveau de journal

Azure Functions vous permet de définir le niveau de seuil à utiliser lors du suivi et de l’affichage de journaux. Pour définir le seuil, utilisez la propriété logging.logLevel dans le fichier host.json. Cette propriété vous permet de définir un niveau par défaut appliqué à toutes les fonctions, ou un seuil pour chaque fonction individuelle. Pour plus d’informations, consultez Comment configurer la surveillance de Azure Functions.

Suivre les données personnalisées

Par défaut, Azure Functions écrit la sortie comme des traces dans Application Insights. Pour plus de contrôle, vous pouvez plutôt utiliser le Kit de développement logiciel (SDK) node.js d’Application Insights pour envoyer des données personnalisées vers votre instance Application Insights.

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

module.exports = async function (context, request) {
    // Use this with 'tagOverrides' to correlate custom logs 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});
};

Le paramètre tagOverrides définit operation_Id sur l’ID d'appel de la fonction. Ce paramètre permet de mettre en corrélation tous les journaux générés automatiquement et personnalisés pour un appel de fonction donné.

Déclencheurs HTTP

Les déclencheurs HTTP et webhook ainsi utilisent les objets de requête et de réponse pour représenter les messages HTTP.

Les déclencheurs HTTP et webhook utilisent des objets HttpRequest et HttpResponse pour représenter les messages HTTP. Les classes représentent un sous-ensemble de la norme de récupération (fetch), à l’aide du package undici de Node.js.

Demande HTTP

Les requêtes sont accessibles de plusieurs façons :

  • Comme deuxième argument de votre fonction :

    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${request.url}"`);
    
  • À partir de la propriété context.req :

    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${context.req.url}"`);
    
  • À partir des liaisons d’entrée nommées : cette option fonctionne de la même façon que toute liaison non HTTP. Le nom de liaison dans function.json doit correspondre à la clé sur context.bindings, ou « request1 » dans l’exemple suivant :

    {
        "name": "request1",
        "type": "httpTrigger",
        "direction": "in",
        "authLevel": "anonymous",
        "methods": [
            "get",
            "post"
        ]
    }
    
    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${context.bindings.request1.url}"`);
    

L’objet HttpRequest dispose des propriétés suivantes :

Propriété Type Description
method string Méthode de requête HTTP utilisée pour appeler cette fonction.
url string URL de requête.
headers Record<string, string> En-têtes de requête HTTP. Cet objet respecte la casse. Il est recommandé d’utiliser plutôt request.getHeader('header-name'), qui ne respecte pas la casse.
query Record<string, string> Clés et valeurs de paramètre de chaîne de requête de l’URL.
params Record<string, string> Clés et valeurs des paramètres de routage.
user HttpRequestUser | null Objet représentant l’utilisateur connecté, via l’authentification Functions, l’authentification SWA ou null lorsqu’aucun utilisateur n’est connecté.
body Buffer | string | any Si le type de média est « application/octet-stream » ou « multipart/* », body est une mémoire tampon. Si la valeur est une chaîne analysable en JSON, body est l’objet analysé. Sinon, body est une chaîne.
rawBody string Corps en tant que chaîne. Malgré le nom, cette propriété ne retourne pas un tampon.
bufferBody Buffer Corps en tant que mémoire tampon.

La requête est accessible en tant que premier argument de votre gestionnaire pour une fonction déclenchée via HTTP.

async (request, context) => {
    context.log(`Http function processed request for url "${request.url}"`);

L’objet HttpRequest dispose des propriétés suivantes :

Propriété Type Description
method string Méthode de requête HTTP utilisée pour appeler cette fonction.
url string URL de requête.
headers Headers En-têtes de requête HTTP.
query URLSearchParams Clés et valeurs de paramètre de chaîne de requête de l’URL.
params Record<string, string> Clés et valeurs des paramètres de routage.
user HttpRequestUser | null Objet représentant l’utilisateur connecté, via l’authentification Functions, l’authentification SWA ou null lorsqu’aucun utilisateur n’est connecté.
body ReadableStream | null Corps en tant que flux accessible en lecture.
bodyUsed boolean Valeur booléenne indiquant si le corps est déjà lu.

Pour accéder au corps d’une requête ou d’une réponse, vous pouvez utiliser les méthodes suivantes :

Méthode Type de retour
arrayBuffer() Promise<ArrayBuffer>
blob() Promise<Blob>
formData() Promise<FormData>
json() Promise<unknown>
text() Promise<string>

Notes

Les fonctions du corps ne peuvent être exécutées qu’une seule fois ; les appels suivants seront résolus avec des chaînes/ArrayBuffers vides.

Réponse HTTP

La réponse peut être définie de plusieurs manières :

  • Définir la propriété context.res :

    module.exports = async function (context, request) {
        context.res = { body: `Hello, world!` };
    
  • Retourner la réponse : si votre fonction est asynchrone et que vous définissez le nom de liaison sur $return dans votre function.json, vous pouvez retourner la réponse directement au lieu de la définir sur context.

    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
    
    module.exports = async function (context, request) {
        return { body: `Hello, world!` };
    
  • Définir la liaisons de sortie nommée : cette option fonctionne de la même façon que toute liaison non HTTP. Le nom de liaison dans function.json doit correspondre à la clé sur context.bindings, ou « response1 » dans l’exemple suivant :

    {
        "type": "http",
        "direction": "out",
        "name": "response1"
    }
    
    module.exports = async function (context, request) {
        context.bindings.response1 = { body: `Hello, world!` };
    
  • Appelercontext.res.send() : Cette option est dépréciée. Elle appelle context.done() implicitement et ne peut pas être utilisée dans une fonction asynchrone.

    module.exports = function (context, request) {
        context.res.send(`Hello, world!`);
    

Si vous créez un objet lors de la définition de la réponse, cet objet doit correspondre à l’interface HttpResponseSimple, qui a les propriétés suivantes :

Propriété Type Description
headers Record<string, string> (facultatif) En-têtes de réponse HTTP.
cookies Cookie[] (facultatif) Cookies de réponse HTTP.
body any (facultatif) Corps de réponse HTTP.
statusCode number (facultatif) Code de statut de la réponse HTTP. Si elle n’est pas définie, la valeur par défaut est 200.
status number (facultatif) Identique à statusCode. Cette propriété est ignorée si statusCode est défini.

Vous pouvez également modifier l’objet context.res sans le remplacer. L’objet context.res par défaut utilise l’interface HttpResponseFull, qui prend en charge les méthodes suivantes en plus des propriétés HttpResponseSimple :

Méthode Description
status() Définit l’état.
setHeader() Définit un champ d’en-tête. REMARQUE : res.set() et res.header() sont également pris en charge et font la même chose.
getHeader() Permet d’obtenir un champ d’en-tête. REMARQUE : res.get() est également pris en charge et fait la même chose.
removeHeader() Supprime un en-tête.
type() Définit l’en-tête « content-type ».
send() Cette méthode est déconseillée. Elle définit le corps et appelle context.done() pour indiquer qu’une fonction de synchronisation est terminée. REMARQUE : res.end() est également pris en charge et fait la même chose.
sendStatus() Cette méthode est déconseillée. Elle définit le code d’état et appelle context.done() pour indiquer qu’une fonction de synchronisation est terminée.
json() Cette méthode est déconseillée. Il définit le « content-type » sur « application/json », définit le corps et appelle context.done() pour indiquer qu’une fonction de synchronisation est terminée.

La réponse peut être définie de plusieurs manières :

  • En tant qu’interface simple avec le type HttpResponseInit : cette option est le moyen le plus concis de retourner des réponses.

    return { body: `Hello, world!` };
    

    Une interface HttpResponseInit possède les propriétés suivantes :

    Propriété Type Description
    body BodyInit (facultatif) Corps de la réponse HTTP sous la forme de ArrayBuffer, AsyncIterable<Uint8Array>, Blob, FormData, Iterable<Uint8Array>, NodeJS.ArrayBufferView, URLSearchParams, null ou string.
    jsonBody any (facultatif) Corps de réponse HTTP sérialisable JSON. Si elle est définie, la propriété HttpResponseInit.body est ignorée au profit de cette propriété.
    status number (facultatif) Code de statut de la réponse HTTP. Si elle n’est pas définie, la valeur par défaut est 200.
    headers HeadersInit (facultatif) En-têtes de réponse HTTP.
    cookies Cookie[] (facultatif) Cookies de réponse HTTP.
  • En tant que classe du type HttpResponse : cette option fournit des méthodes d’assistance pour la lecture et la modification de différentes parties de la réponse, comme les en-têtes.

    const response = new HttpResponse({ body: `Hello, world!` });
    response.headers.set('content-type', 'application/json');
    return response;
    

    La classe HttpResponse accepte un facultatif HttpResponseInit comme argument de son constructeur et possède les propriétés suivantes :

    Propriété Type Description
    status number Code de statut de la réponse HTTP.
    headers Headers En-têtes de réponse HTTP.
    cookies Cookie[] Cookies de réponse HTTP.
    body ReadableStream | null Corps en tant que flux accessible en lecture.
    bodyUsed boolean Valeur booléenne indiquant si le corps a déjà été lu.

Flux HTTP (préversion)

Les flux HTTP sont une fonctionnalité qui facilite le traitement de données volumineuses, le flux de réponses d’OpenAI, la diffusion de contenu dynamique et la prise en charge d’autres scénarios HTTP fondamentaux. Cela vous permet de diffuser en continu des requêtes vers et des réponses à partir de points de terminaison HTTP dans votre application de fonction Node.js. Utilisez des flux HTTP dans des scénarios où votre application nécessite un échange et une interaction en temps réel entre le client et le serveur via HTTP. Vous pouvez également utiliser des flux HTTP pour obtenir les meilleures performances et la fiabilité optimale pour vos applications lors de l’utilisation de HTTP.

Les flux HTTP sont actuellement en préversion.

Important

Les flux HTTP ne sont pas pris en charge dans le modèle v3. Mettre à niveau vers le modèle v4 pour utiliser la fonctionnalité de diffusion en continu HTTP.

Les types HttpRequest et HttpResponse existants dans le modèle de programmation v4 prennent déjà en charge différentes façons de gérer le corps du message, y compris en tant que flux.

Prérequis

Activer les flux

Procédez comme suit pour activer les flux HTTP dans votre application de fonction dans Azure et dans vos projets locaux :

  1. Si vous envisagez de diffuser en continu de grandes quantités de données, modifiez le paramètre de FUNCTIONS_REQUEST_BODY_SIZE_LIMIT dans Azure. La taille maximale de corps par défaut autorisée est 104857600, ce qui limite vos demandes à une taille d’environ 100 Mo.

  2. Pour le développement local, ajoutez également FUNCTIONS_REQUEST_BODY_SIZE_LIMIT au fichier local.settings.json.

  3. Ajoutez le code suivant à votre application dans n’importe quel fichier inclus par votre champ principal.

    const { app } = require('@azure/functions'); 
    
    app.setup({ enableHttpStream: true });
    

Exemples de flux

Cet exemple montre une fonction déclenchée par HTTP qui reçoit des données via une requête HTTP POST, et la fonction diffuse ces données vers un fichier de sortie spécifié :

const { app } = require('@azure/functions');
const { createWriteStream } = require('fs');
const { Writable } = require('stream');

app.http('httpTriggerStreamRequest', {
    methods: ['POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        const writeStream = createWriteStream('<output file path>');
        await request.body.pipeTo(Writable.toWeb(writeStream));

        return { body: 'Done!' };
    },
});

Cet exemple montre une fonction déclenchée par HTTP qui diffuse le contenu d’un fichier en tant que réponse aux requêtes HTTP GET entrantes :

const { app } = require('@azure/functions');
const { createReadStream } = require('fs');

app.http('httpTriggerStreamResponse', {
    methods: ['GET'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        const body = createReadStream('<input file path>');

        return { body };
    },
});

Considérations relatives au flux de données

  • L’objet request.params n’est pas pris en charge lors de l’utilisation de flux HTTP pendant la préversion. Reportez-vous à ce problème GitHub pour plus d’informations et pour obtenir des suggestions de solution de contournement.

  • Utilisez request.body pour obtenir le maximum d’avantages de l’utilisation de flux. Vous pouvez toujours continuer à utiliser des méthodes comme request.text(), qui retournent toujours le corps sous forme de chaîne.

Hooks

Les hooks ne sont pas pris en charge dans le modèle v3. Effectuez une mise à niveau vers le modèle v4 pour utiliser des hooks.

Utilisez un hook pour exécuter du code à différents points du cycle de vie d’Azure Functions. Les hooks sont exécutés dans l’ordre dans lequel ils sont inscrits et peuvent être inscrits à partir de n’importe quel fichier de votre application. Il existe actuellement deux étendues de hooks, de niveau « application » et de niveau « appel ».

Crochets d’appel

Les crochets d’appel sont exécutés une fois par appel de votre fonction, soit avant, dans un hook preInvocation, soit après, dans un hook postInvocation. Par défaut, votre hook s’exécute pour tous les types de déclencheurs, mais vous pouvez également filtrer par type. L’exemple suivant montre comment inscrire un hook d’appel et filtrer par type de déclencheur :

const { app } = require('@azure/functions');

app.hook.preInvocation((context) => {
    if (context.invocationContext.options.trigger.type === 'httpTrigger') {
        context.invocationContext.log(
            `preInvocation hook executed for http function ${context.invocationContext.functionName}`
        );
    }
});

app.hook.postInvocation((context) => {
    if (context.invocationContext.options.trigger.type === 'httpTrigger') {
        context.invocationContext.log(
            `postInvocation hook executed for http function ${context.invocationContext.functionName}`
        );
    }
});

Le premier argument du gestionnaire de hooks est un objet de contexte spécifique à ce type de hook.

L’objet PreInvocationContext dispose des propriétés suivantes :

Propriété Description
inputs Les arguments passés à l’appel.
functionHandler Le gestionnaire de fonction pour l’appel. Les modifications apportées à cette valeur affectent la fonction elle-même.
invocationContext L’objet de contexte d’appel passé à la fonction.
hookData L’emplacement recommandé pour stocker et partager des données entre des hooks dans la même étendue. Vous devez utiliser un nom de propriété unique afin qu’il ne soit pas en conflit avec les données d’autres hooks.

L’objet PostInvocationContext dispose des propriétés suivantes :

Propriété Description
inputs Les arguments passés à l’appel.
result Résultat de la fonction. Les modifications apportées à cette valeur affectent le résultat global de la fonction.
error L’erreur levée par la fonction, ou null/undefined s’il n’y a pas d’erreur. Les modifications apportées à cette valeur affectent le résultat global de la fonction.
invocationContext L’objet de contexte d’appel passé à la fonction.
hookData L’emplacement recommandé pour stocker et partager des données entre des hooks dans la même étendue. Vous devez utiliser un nom de propriété unique afin qu’il ne soit pas en conflit avec les données d’autres hooks.

Hooks d’application

Les hooks d’application sont exécutés une fois par instance de votre application, soit au démarrage dans un hook appStart, soit lors de l’arrêt dans un hook appTerminate. Les hooks d’arrêt d’application ont un temps limité pour s’exécuter et ne s’exécutent pas dans tous les scénarios.

Le runtime Azure Functions ne prend actuellement pas en charge la journalisation contextuelle en dehors d’un appel. Utilisez le package npm Application Insights pour journaliser les données pendant les hooks au niveau de l’application.

L’exemple suivant enregistre les hooks d’application  :

const { app } = require('@azure/functions');

app.hook.appStart((context) => {
    // add your logic here
});

app.hook.appTerminate((context) => {
    // add your logic here
});

Le premier argument du gestionnaire de hooks est un objet de contexte spécifique à ce type de hook.

L’objet AppStartContext dispose des propriétés suivantes :

Propriété Description
hookData L’emplacement recommandé pour stocker et partager des données entre des hooks dans la même étendue. Vous devez utiliser un nom de propriété unique afin qu’il ne soit pas en conflit avec les données d’autres hooks.

L’objet AppTerminateContext dispose des propriétés suivantes :

Propriété Description
hookData L’emplacement recommandé pour stocker et partager des données entre des hooks dans la même étendue. Vous devez utiliser un nom de propriété unique afin qu’il ne soit pas en conflit avec les données d’autres hooks.

Mise à l’échelle et accès concurrentiel

Par défaut, Azure Functions surveille automatiquement la charge dans votre application et crée des instances d’hôte supplémentaires pour Node.js, si nécessaire. Azure Functions utilise des seuils intégrés (non configurables par l’utilisateur) pour différents types de déclencheurs afin de décider quand ajouter des instances, comme l’ancienneté des messages et la taille de la file d’attente pour QueueTrigger. Pour plus d’informations, consultez Fonctionnement des plans Consommation et Premium.

Ce comportement de mise à l’échelle est suffisant pour de nombreuses applications Node.js. Pour les applications utilisant le processeur de manière intensive, vous pouvez améliorer encore plus les performances en utilisant plusieurs processus Worker de langage. Vous pouvez augmenter le nombre de processus Worker par hôte de la valeur par défaut 1 jusqu’à un maximum de 10 à l’aide du paramètre d’application FUNCTIONS_WORKER_PROCESS_COUNT. Azure Functions essaie ensuite de distribuer uniformément les appels de fonction simultanés à ces différents Workers. Ce comportement réduit la probabilité qu’une fonction gourmande en ressources CPU bloque l’exécution des autres fonctions. Le paramètre s’applique à chaque hôte créé par Azure Functions lors de la mise à l’échelle de votre application pour répondre à la demande.

Avertissement

Utilisez le paramètre FUNCTIONS_WORKER_PROCESS_COUNT avec précaution. Plusieurs processus s’exécutant dans la même instance peuvent entraîner un comportement imprévisible et augmenter les temps de chargement des fonctions. Si vous utilisez ce paramètre, il est vivement recommandé de compenser ces inconvénients en effectuant l’exécution à partir d’un fichier de package.

Version de nœud

Vous pouvez voir la version que le runtime utilise en journalisant process.version depuis n’importe quelle fonction. Consultez supported versions pour la liste des versions Node.js prises en charge par chaque modèle de programmation.

Définition de la version de Node

La façon dont vous mettez à niveau votre version Node.js dépend du système d’exploitation sur lequel votre application de fonction s’exécute.

Lors de l’exécution sur Windows, la version Node.js est définie par le paramètre d’application WEBSITE_NODE_DEFAULT_VERSION. Ce paramètre peut être mis à jour à l’aide d’Azure CLI ou dans le Portail Azure.

Pour plus d’informations sur les versions Node.js disponibles, consultez Versions prises en charge.

Avant de mettre à niveau votre version Node.js, assurez-vous que votre application de fonction s’exécute sur la dernière version du runtime Azure Functions. Si vous devez mettre à niveau votre version du runtime, consultez Migrer des applications de Azure Functions version 3.x vers la version 4.x.

Exécutez la commande Azure CLI az functionapp config appsettings set pour mettre à jour la version Node.js de votre application de fonction s’exécutant sur Windows :

az functionapp config appsettings set  --settings WEBSITE_NODE_DEFAULT_VERSION=~20 \
 --name <FUNCTION_APP_NAME> --resource-group <RESOURCE_GROUP_NAME> 

Cela définit le paramètre d’application WEBSITE_NODE_DEFAULT_VERSION la version LTS prise en charge de ~20.

Une fois les modifications apportées, votre application de fonction redémarre. Pour en savoir plus sur la prise en charge de Functions pour Node.js, consultez Stratégie de support du runtime de langage.

Variables d'environnement

Les variables d’environnement peuvent être utiles pour les secrets opérationnels (chaînes de connexion, clés, points de terminaison, etc.) ou les paramètres environnementaux tels que les variables de profilage. Vous pouvez ajouter des variables d’environnement dans vos environnements locaux et cloud, et y accéder via process.env dans le code de votre fonction.

L’exemple suivant journalise la variable d’environnement WEBSITE_SITE_NAME :

module.exports = async function (context) {
    context.log(`WEBSITE_SITE_NAME: ${process.env["WEBSITE_SITE_NAME"]}`);
}
async function timerTrigger1(myTimer, context) {
    context.log(`WEBSITE_SITE_NAME: ${process.env["WEBSITE_SITE_NAME"]}`);
}

Dans un environnement de développement local

Lorsque vous effectuez une exécution locale, votre projet Functions comprend un local.settings.json fichier dans lequel vous stockez vos variables d’environnement dans l’objet Values.

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "",
    "FUNCTIONS_WORKER_RUNTIME": "node",
    "CUSTOM_ENV_VAR_1": "hello",
    "CUSTOM_ENV_VAR_2": "world"
  }
}

Dans un environnement cloud Azure

Lorsque vous effectuez une exécution dans Azure, l’application de fonction vous permet de définir et d’utiliser des paramètres d’application comme des chaînes de connexion de service, tout en exposant ces paramètres sous forme de variables d’environnement pendant l’exécution.

Plusieurs méthodes sont possibles pour ajouter, mettre à jour et supprimer des paramètres d’une application de fonction :

Les changements apportés aux paramètres d’application de fonction nécessitent le redémarrage de votre application de fonction.

Variables d’environnement worker

Il existe plusieurs variables d’environnement Functions spécifiques à Node.js :

languageWorkers__node__arguments

Ce paramètre vous permet de spécifier des arguments personnalisés lors du démarrage de votre processus Node.js. Il est le plus souvent utilisé localement pour démarrer le worker en mode débogage, mais peut également être utilisé dans Azure si vous avez besoin d’arguments personnalisés.

Avertissement

Si possible, évitez d’utiliser languageWorkers__node__arguments dans Azure, car cela peut avoir un effet négatif sur les heures de démarrage à froid. Au lieu d’utiliser des workers chauffées au préalable, le runtime doit démarrer un nouveau worker à partir de zéro avec vos arguments personnalisés.

logging__logLevel__Worker

Ce paramètre ajuste le niveau de journal par défaut pour les journaux worker spécifiques à Node.js. Par défaut, seuls les journaux d’avertissement ou d’erreur sont affichés, mais vous pouvez le définir sur information ou sur debug pour faciliter le diagnostic des problèmes liés au worker Node.js. Pour plus d’informations, consultez Configuration des niveaux de journal.

Modules ECMAScript (préversion)

Notes

Comme les modules ECMAScript sont actuellement une fonctionnalité d’évaluation dans Node.js 14 et ultérieur Azure Functions.

Les modules ECMAScript (modules ES) sont le nouveau système de modules standard officiel pour Node.js. Jusqu’à présent, les exemples de code de cet article utilisent la syntaxe CommonJS. Lorsque vous exécutez Azure Functions dans Node.js 14 ou une version ultérieure, vous pouvez choisir d’écrire vos fonctions en utilisant la syntaxe des modules ES.

Pour utiliser les modules ES dans une fonction, modifiez son nom de fichier pour qu’elle utilise une extension .mjs. L’exemple de fichier index.mjs suivant est une fonction déclenchée par HTTP qui utilise la syntaxe des modules ES pour importer la bibliothèque uuid et renvoyer une valeur.

import { v4 as uuidv4 } from 'uuid';

async function httpTrigger1(context, request) {
    context.res.body = uuidv4();
};

export default httpTrigger;
import { v4 as uuidv4 } from 'uuid';

async function httpTrigger1(request, context) {
    return { body: uuidv4() };
};

app.http('httpTrigger1', {
    methods: ['GET', 'POST'],
    handler: httpTrigger1
});

Configurer le point d’entrée de la fonction

Vous pouvez utiliser les propriétés function.jsonscriptFile et entryPoint pour configurer l’emplacement et le nom de votre fonction exportée. La propriété scriptFile est obligatoire quand vous utilisez TypeScript et doit pointer vers le code JavaScript compilé.

Utilisation de scriptFile

Par défaut, une fonction JavaScript est exécutée à partir de index.js, fichier qui partage le même répertoire parent que son function.json correspondant.

Vous pouvez utiliser la propriété scriptFile pour obtenir une structure de dossiers semblable à l’exemple suivant :

<project_root>/
 | - node_modules/
 | - myFirstFunction/
 | | - function.json
 | - lib/
 | | - sayHello.js
 | - host.json
 | - package.json

Le function.json pour myFirstFunction doit inclure une propriété scriptFile qui pointe vers le fichier contenant la fonction exportée à exécuter.

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

Utilisation de entryPoint

Dans le modèle v3, une fonction doit être exportée à l’aide de module.exports afin qu’elle soit trouvée et exécutée. Par défaut, la fonction qui s’exécute quand elle est déclenchée est la seule exportation à partir de ce fichier, l’exportation nommée run ou l’exportation nommée index. L’exemple suivant définit entryPoint dans function.json sur une valeur personnalisée, « logHello » :

{
  "entryPoint": "logHello",
  "bindings": [
    ...
  ]
}
async function logHello(context) {
    context.log('Hello, world!');
}

module.exports = { logHello };

Débogage local

Il est recommandé d’utiliser VS Code pour le débogage local, qui démarre automatiquement votre processus Node.js en mode débogage et effectue l’attachement au processus pour vous. Pour plus d’informations, consultez Exécuter la fonction localement.

Si vous utilisez un autre outil pour le débogage ou si vous souhaitez démarrer manuellement votre processus Node.js en mode débogage, ajoutez "languageWorkers__node__arguments": "--inspect" sous Values dans votre fichier local.settings.json. L’argument --inspect indique à Node.js d’écouter un client de débogage sur le port 9229 par défaut. Pour plus d’informations, consultez le guide de débogage Node.js.

Recommandations

Cette section décrit plusieurs modèles impactants pour les applications Node.js que nous vous recommandons de suivre.

Choisir des plans App Service à processeur virtuel unique

Lorsque vous créez une application de fonction qui utilise le plan App Service, nous vous recommandons de sélectionner un plan à processeur virtuel unique plutôt qu’un plan à plusieurs processeurs virtuels. À l’heure actuelle, Functions exécute les fonctions Node.js plus efficacement sur des machines virtuelles à processeur virtuel unique. Le recours à de plus grandes machines virtuelles ne produit pas les améliorations de performances attendues. Le cas échéant, vous pouvez effectuer un scale-out manuellement en ajoutant des instances de machine virtuelle à processeur virtuel unique, ou vous pouvez activer la mise à l’échelle automatique. Pour plus d’informations, consultez Mettre à l’échelle le nombre d’instances manuellement ou automatiquement.

Effectuer l’exécution à partir d’un fichier de package

Lorsque vous développez des fonctions Azure Functions dans le modèle d’hébergement serverless, les démarrages à froid sont une réalité. Démarrage à froid fait référence à la première fois que votre application de fonction démarre après une période d’inactivité, prenant plus de temps au démarrage. En particulier pour les applications Node.js avec de grandes arborescences de dépendances, le démarrage à froid peut prendre un temps considérable. Pour accélérer le processus de démarrage à froid, exécutez vos fonctions en tant que fichier de package lorsque cela est possible. De nombreuses méthodes de déploiement utilisent ce modèle par défaut, mais si vous avez des démarrages à froid volumineux, vous devez vérifier pour vous assurer de l’exécuter de cette façon.

Utiliser un client statique unique

Lorsque vous utilisez un client spécifique du service dans une application Azure Functions, ne créez pas de nouveau client à chaque appel de fonction, car vous pouvez atteindre les limites de connexions. Créez plutôt un client statique unique dans l’étendue globale. Pour plus d’informations, consultez l’article relatif à la gestion des connexions dans Azure Functions.

Utiliser async et await

Lors de l’écriture d’Azure Functions dans Node.js, vous devez écrire le code à l’aide des mots clés async et await. L’écriture de code à l’aide de async et await au lieu de rappels ou de .then et .catch avec Promises permet d’éviter deux problèmes courants :

  • Levée d’exceptions non interceptées qui bloquent le processus Node.js, affectant ainsi potentiellement l’exécution d’autres fonctions.
  • Comportement inattendu, comme des journaux manquants dans context.log, en raison d’appels asynchrones inattendus.

Dans l’exemple suivant, la méthode asynchrone fs.readFile est appelée avec une fonction de rappel d’erreur en premier comme second paramètre. Ce code entraîne les deux problèmes précédemment mentionnés. Une exception qui n’est pas interceptée explicitement dans l’étendue appropriée peut planter l’ensemble du processus (problème n°1). Le retour sans s’assurer que le rappel se termine signifie que la réponse http aura parfois un corps vide (problème n° 2).

// DO NOT USE THIS CODE
const { app } = require('@azure/functions');
const fs = require('fs');

app.http('httpTriggerBadAsync', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        let fileData;
        fs.readFile('./helloWorld.txt', (err, data) => {
            if (err) {
                context.error(err);
                // BUG #1: This will result in an uncaught exception that crashes the entire process
                throw err;
            }
            fileData = data;
        });
        // BUG #2: fileData is not guaranteed to be set before the invocation ends
        return { body: fileData };
    },
});

Dans l’exemple suivant, la méthode asynchrone fs.readFile est appelée avec une fonction de rappel d’erreur en premier comme second paramètre. Ce code entraîne les deux problèmes précédemment mentionnés. Une exception qui n’est pas interceptée explicitement dans l’étendue appropriée peut planter l’ensemble du processus (problème n°1). Un appel à la méthode dépréciée context.done() en dehors de l’étendue du rappel peut signaler que la fonction est terminée avant la lecture du fichier (problème n°2). Dans cet exemple, un appel context.done() trop tôt entraîne des entrées de journal manquantes avec 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();
}

Utilisez les mots clés async et await pour éviter ces deux problèmes. La plupart des API de l’écosystème Node.js ont été converties pour prendre en charge les promesses sous une forme donnée. Par exemple, à compter de la version 14, Node.js fournit une API fs/promises pour remplacer l’API de rappel fs.

Dans l’exemple suivant, les exceptions non prises en charge levées pendant l’exécution de la fonction entraînent uniquement un échec de l’appel individuel qui a levé l’exception. Le mot clé await implique que les étapes après readFile ne s’exécutent que lorsqu’elle est terminée.

// Recommended pattern
const { app } = require('@azure/functions');
const fs = require('fs/promises');

app.http('httpTriggerGoodAsync', {
    methods: ['GET', 'POST'],
    authLevel: 'anonymous',
    handler: async (request, context) => {
        try {
            const fileData = await fs.readFile('./helloWorld.txt');
            return { body: fileData };
        } catch (err) {
            context.error(err);
            // This rethrown exception will only fail the individual invocation, instead of crashing the whole process
            throw err;
        }
    },
});

Avec async et await, vous n’avez pas besoin d’appeler le rappel context.done() non plus.

// Recommended pattern
const fs = require('fs/promises');

module.exports = async function (context) {
    let data;
    try {
        data = await fs.readFile('./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}`);
}

Résoudre les problèmes

Consultez le Guide de résolution des problèmes Node.js.

Étapes suivantes

Pour plus d’informations, consultez les ressources suivantes :