Guía para el desarrollador de JavaScript para Azure Functions

Esta guía contiene información detallada para ayudarle a desarrollar correctamente Azure Functions con JavaScript.

Como desarrollador Express.js, Node.js o JavaScript, si no está familiarizado con Azure Functions, considere la posibilidad de leer primero uno de los siguientes artículos:

Introducción Conceptos Aprendizaje guiado

Fundamentos de las funciones de JavaScript

Una función de JavaScript (Node.js) es una function exportada que se ejecuta al desencadenarse (function). El primer argumento que se pasa a cada función es un objeto context que se usa para enviar y recibir datos de enlace, para el registro y para la comunicación con el entorno de ejecución.

Estructura de carpetas

La estructura de carpetas necesaria para un proyecto de JavaScript tiene el siguiente aspecto. Este valor predeterminado se puede cambiar. Para más información, consulte la sección scriptFile a continuación.

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

En la raíz del proyecto, hay un archivo host.json compartido que se puede usar para configurar la aplicación de función. Cada función tiene una carpeta con su propio archivo de código (.js) y archivo de configuración de enlace (function.json). El nombre del directorio primario de function.json siempre es el nombre de la función.

Las extensiones de enlace necesarias en la versión 2.x del tiempo de ejecución de Functions se definen en el archivo , con los archivos de biblioteca de la carpeta bin. Al desarrollar de forma local, debe registrar las extensiones de enlace. Al desarrollar funciones en Azure Portal, este registro se realiza automáticamente.

Exportación de una función

Deben exportarse las funciones de JavaScript mediante module.exports (o exports). La función exportada debe ser una función JavaScript que se ejecute al desencadenarse.

De forma predeterminada, el tiempo de ejecución de Functions busca la función en index.js, donde index.js comparte el mismo directorio primario que su function.json correspondiente. En el caso predeterminado, la función exportada debe ser la única exportación de su archivo, o bien la exportación denominada run o index. Para configurar la ubicación del archivo y el nombre de exportación de la función, obtenga información sobre la configuración del punto de entrada de la función a continuación.

Varios argumentos se pasan a la función exportada durante la ejecución. El primer argumento que toma siempre es un objeto context.

Cuando se usa la declaración async function o las promesas de JavaScript sin formato en la versión 2.x, 3.x o 4.x de Functions Runtime, no tiene que llamar explícitamente a la devolución de llamada context.done para indicar que la función se ha completado. La función se completa cuando la función asincrónica o la promesa exportada se completa.

El ejemplo siguiente es una función simple que registra que se ha desencadenado y completa la ejecución inmediatamente.

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

Al exportar una función asincrónica, también puede configurar en enlace de salida para tomar el valor return. Esto se recomienda si solo tiene un enlace de salida.

Devolución de la función

Para asignar una salida mediante return, cambie la propiedad name a $return en function.json.

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

En este caso, la función debe tener el aspecto del ejemplo siguiente:

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!"
    };
}

Enlaces

En JavaScript, los enlaces se configuran y definen en el archivo function.json de una función. Las funciones interactúan con los enlaces de varias maneras.

Entradas

Las entradas se dividen en dos categorías Azure Functions: una es la entrada del desencadenador y la otra es una entrada adicional. Una función puede leer los enlaces del desencadenador y de entrada (enlaces de direction === "in") de tres maneras:

  • [Recomendada] Como parámetros pasados a la función. Se pasan a la función en el mismo orden en que se definen en function.json. La propiedad name definida en el archivo function.json no tiene que coincidir con el nombre del parámetro, aunque debería hacerlo.

    module.exports = async function(context, myTrigger, myInput, myOtherInput) { ... };
    
  • Como miembros del objeto context.bindings. Cada miembro se denomina mediante la propiedad name definida en el archivo 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);
    };
    

Salidas

Las salidas (enlaces de direction === "out") se pueden escribir mediante una función de varias maneras. En todos los casos, la propiedad name del enlace tal como se define en el archivo name corresponde al nombre del miembro de objeto escrito en la función.

Puede asignar datos a los enlaces de salida de una de las maneras siguientes (no combine estos métodos):

  • [Recomendado para varias salidas] Devolución de un objeto. Si usa una función de devolución asincrónica o de promesa, puede devolver un objeto con datos de salida asignados. En el ejemplo siguiente, los enlaces de salida se denominan "httpResponse" y "queueOutput" en el archivo function.json.

    module.exports = async function(context) {
        let retMsg = 'Hello, world!';
        return {
            httpResponse: {
                body: retMsg
            },
            queueOutput: retMsg
        };
    };
    
  • [Recomendado para una salida única] Devolución de un valor directamente y uso del nombre de enlace $return. Este método solo funciona con las funciones de devolución asincrónicas o de promesa. Vea el ejemplo de Exportación de una función asincrónica.

  • Asignación de valores a . Puede asignar valores directamente a context.bindings.

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

Tipo de datos de enlaces

Para definir el tipo de datos para un enlace de entrada, use la propiedad dataType de la definición del enlace. Por ejemplo, para leer el contenido de una solicitud HTTP en formato binario, use el tipo binary:

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

Las opciones para dataType son: binary, stream y string.

objeto de contexto

El entorno de ejecución usa un objeto context para pasar los datos desde la función y hacia esta. Se usa para leer y establecer los datos de los enlaces y para escribir en los registros; el objeto context siempre es el primer parámetro que se pasa a una función.

module.exports = async function(context){

    // function logic goes here

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

El contexto que se pasa a la función expone una propiedad executionContext, que es un objeto con las siguientes propiedades:

Nombre de propiedad Tipo Descripción
invocationId String Proporciona un identificador único para la invocación específica de la función.
functionName String Proporciona el nombre de la función en ejecución.
functionDirectory String Proporciona el directorio de la aplicación de funciones.

El ejemplo siguiente muestra la forma de devolver el valor invocationId.

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

Propiedad context.bindings

context.bindings

Devuelve un objeto con nombre que se usa para leer o asignar datos de enlace. A los datos de enlace de entrada y desencadenador se puede acceder mediante las propiedades de lectura en context.bindings. Los datos de enlace de salida se pueden asignar mediante la adición de datos a context.bindings.

Por ejemplo, las siguientes definiciones de enlace del archivo function.json permiten acceder al contenido de una cola desde context.bindings.myInput y asignar salidas a una cola mediante 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 };

En una función sincrónica, puede definir datos de enlace de salida mediante el método context.done en lugar del objeto context.binding (consulte a continuación).

Propiedad context.bindingData

context.bindingData

Devuelve un objeto con nombre que contiene los metadatos de desencadenar y los datos de invocación de función (invocationId, sys.methodName, sys.utcNow y sys.randGuid). Para obtener un ejemplo de metadatos de desencadenador, consulte este ejemplo de centros de eventos.

Método context.done

En las versiones 2.x, 3.x y 4.x, la función debe marcarse como asincrónica incluso si no se espera ninguna llamada de función dentro de la función y esta no necesita llamar al método context.done para indicar el final de la función.

//you don't need an awaited function call inside to use async
module.exports = async function (context, req) {
    context.log("you don't need an awaited function call inside to use async")
};

Método context.log

context.log(message)

Permite escribir en los registros de la función de streaming en el nivel de seguimiento predeterminado, con otros niveles de registro disponibles. El registro del seguimiento se describe con más detalle en la sección siguiente.

Escritura de la salida del seguimiento en los registros

En Functions, use los métodos context.log para escribir la salida de seguimiento en los registros y en la consola. Cuando se llama a context.log(), el mensaje se escribe en los registros del nivel de seguimiento predeterminado, que es el nivel de seguimiento de context.log(). Functions se integra con Azure Application Insights para capturar mejor los registros de la aplicación de funciones. Application Insights, que forma parte de Azure Monitor, proporciona funciones para la recopilación, la representación visual y el análisis de los datos de telemetría de la aplicación y de las salidas del seguimiento. Para más información, consulte Supervisión de Azure Functions.

En el ejemplo siguiente se escribe un registro en el nivel de seguimiento de información que incluya el identificador de invocación:

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

Todos los métodos context.log admiten el mismo formato de parámetro que el context.log de Node.js. Considere el siguiente código que escribe registros de función mediante el nivel de seguimiento predeterminado:

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

También puede escribir el mismo código con el formato siguiente:

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

Nota

No use console.log para escribir salidas de seguimiento. Como la salida de console.log se captura en el nivel de la aplicación de funciones, no está asociada a ninguna invocación de función específica y no aparece en los registros de una función específica. Además, la versión 1.x del entorno de ejecución de Functions no admite el uso de console.log para escribir en la consola.

Niveles de seguimiento

Además del nivel predeterminado, están disponibles los siguientes métodos de registro para permitirle escribir registros de funciones en niveles de seguimiento específicos.

Método Descripción
context.log.error(mensaje) Escribe un evento de nivel de error en los registros.
context.log.warn(mensaje) Escribe un evento de nivel de advertencia en los registros.
context.log.info(mensaje) Escribe en el registro de nivel de información o inferior.
context.log.verbose(mensaje) Escribe en el registro de nivel detallado.

En el ejemplo siguiente se escribe el mismo registro en el nivel de seguimiento de advertencia, en lugar de en el nivel de información:

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

Dado que error es el nivel de seguimiento más alto, este seguimiento se escribe en la salida en todos los niveles de seguimiento, siempre y cuando el registro esté habilitado.

Configuración del nivel de seguimiento para el registro

Functions permite definir el nivel de seguimiento de umbral para escribir en los registros o en la consola. La configuración del umbral específico depende de la versión del entorno de Functions.

Para establecer el umbral para los seguimientos que se escriben en los registros, use la propiedad logging.logLevel en el archivo host.json. Este objeto JSON le permite definir un umbral predeterminado para todas las funciones de la aplicación, además de definir umbrales específicos para funciones individuales. Para más información, consulte Configuración de la supervisión para Azure Functions.

Registro de la telemetría personalizada

De forma predeterminada, Functions escribe las salidas como seguimientos en Application Insights. Para obtener más control, puede usar en su lugar el SDK de Node.js para Application Insights para enviar los datos de telemetría personalizados a la instancia de Application Insights.

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

module.exports = async 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});
};

El parámetro tagOverrides establece operation_Id en el identificador de invocación de la función. Esta configuración le permite correlacionar toda la telemetría personalizada y generada automáticamente para una determinada invocación de función.

Desencadenadores y enlaces HTTP

Los desencadenadores HTTP y de webhook trigger y los enlaces de salida HTTP usan objetos de solicitud y respuesta para representar la mensajería HTTP.

Objeto de solicitud

El objeto context.req (solicitud) tiene las siguientes propiedades:

Propiedad Descripción
body Objeto que contiene el cuerpo de la solicitud.
headers Objeto que contiene los encabezados de la solicitud.
method Método HTTP de la solicitud.
originalUrl Dirección URL de la solicitud.
params Objeto que contiene los parámetros de enrutamiento de la solicitud.
consulta Objeto que contiene los parámetros de consulta.
rawBody Cuerpo del mensaje como una cadena.

Objeto de respuesta

El objeto context.res (respuesta) tiene las siguientes propiedades:

Propiedad Descripción
body Objeto que contiene el cuerpo de la respuesta.
headers Objeto que contiene los encabezados de la respuesta.
isRaw Indica que se omite el formato en la respuesta.
status Código de estado HTTP de la respuesta.
cookies Matriz de objetos de cookie HTTP que se establece en la respuesta. Un objeto de cookie HTTP tiene name, value y otras propiedades de cookie, como maxAge o sameSite.

Acceso a solicitudes y respuestas

Cuando se trabaja con desencadenadores HTTP, hay varias maneras de acceder a los objetos de solicitud y respuesta HTTP:

  • Desde las propiedades req y res del objeto context. De esta manera, puede usar el patrón convencional para acceder a los datos HTTP desde el objeto de contexto, en lugar de tener que utilizar el patrón context.bindings.name completo. En el ejemplo siguiente se muestra cómo acceder a los objetos req y res en context:

    // 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!' }; 
    
  • Desde los enlaces de entrada y salida con nombre. De esta manera, el desencadenador HTTP y los enlaces funcionan igual que cualquier otro enlace. En el ejemplo siguiente se establece el objeto de respuesta mediante un enlace response con nombre:

    {
        "type": "http",
        "direction": "out",
        "name": "response"
    }
    
    context.bindings.response = { status: 201, body: "Insert succeeded." };
    
  • [Solo respuesta] Llamando a . Se crea una respuesta HTTP con la entrada body como cuerpo de la respuesta. Se llama a context.done() implícitamente.

  • [Solo respuesta] Devolviendo la respuesta. Un nombre de enlace especial de $return permite asignar el valor devuelto de la función al enlace de salida. El enlace de salida HTTP siguiente define un parámetro de salida $return:

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

    En una función de la versión 2.x o posterior, puede devolver el objeto de respuesta directamente:

    return { status: 201, body: "Insert succeeded." };
    

Las claves de solicitud y respuesta están en minúsculas.

Escalado y simultaneidad

De forma predeterminada, Azure Functions supervisa automáticamente la carga en la aplicación y crea instancias de host adicionales para Node.js según sea necesario. Functions usa umbrales integrados (no configurables por el usuario) en diferentes tipos de desencadenadores para decidir cuándo se deben agregar instancias, como la antigüedad de los mensajes y el tamaño de la cola para QueueTrigger. Para obtener más información, consulte Cómo funcionan los planes de consumo y Premium.

Este comportamiento de escalado es suficiente para muchas aplicaciones de Node.js. En las aplicaciones dependientes de la CPU, puede mejorar aún más el rendimiento mediante el uso de varios procesos de trabajo de lenguaje.

De forma predeterminada, cada instancia de host de Functions tiene un único proceso de trabajo de lenguaje. Puede aumentar el número de procesos de trabajo por host (hasta 10) mediante la configuración de la aplicación FUNCTIONS_WORKER_PROCESS_COUNT. Al hacerlo, Azure Functions intenta distribuir uniformemente las invocaciones de función simultáneas en estos trabajos. Esto hace que sea menos probable que una función que consume mucha CPU bloquee la ejecución de otras funciones.

FUNCTIONS_WORKER_PROCESS_COUNT se aplica a cada host que Functions crea al escalar horizontalmente la aplicación para satisfacer la demanda.

Versión de Node

En la tabla siguiente se muestran las versiones actuales de Node. js compatibles para cada versión principal del runtime de Functions, por sistema operativo:

Versión de Functions Versión de Node (Windows) Versión de Node (Linux)
4.x (recomendado) ~18(versión preliminar)
~16
~14
node|18(versión preliminar)
node|16
node|14
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 (bloqueado por el entorno de tiempo de ejecución) N/D

Puede ver la versión actual que el entorno de tiempo de ejecución usa mediante el registro de process.version desde cualquier función.

Especificación de la versión de Node

Para las aplicaciones de funciones de Windows, seleccione el destino de la versión en Azure estableciendo la WEBSITE_NODE_DEFAULT_VERSIONWEBSITE_NODE_DEFAULT_VERSION en una versión compatible con LTS, como ~16.

Para obtener más información sobre la directiva de compatibilidad con el entorno de ejecución de Azure Functions, vea este artículo.

Administración de dependencias

Para poder utilizar bibliotecas de la comunidad en el código de JavaScript, como se muestra en el ejemplo siguiente, debe asegurarse de que todas las dependencias estén instaladas en Function App en Azure.

// Import the underscore.js library
const _ = require('underscore');

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

Nota

Debe definir un archivo package.json en la raíz de Function App. La definición del archivo permite que todas las funciones de la aplicación compartan los mismos paquetes almacenados en caché, de tal manera que se ofrece el mejor rendimiento. Cuando hay conflictos con una versión, puede resolverlo mediante la adición de un archivo package.json en la carpeta de una función específica.

Al implementar aplicaciones de función desde el control de código fuente, cualquier archivo package.json presente en el repositorio desencadenará un elemento npm install en su carpeta durante la implementación. Sin embargo, al implementarlas mediante el portal o la CLI, tendrá que instalar los paquetes manualmente.

Hay dos maneras de instalar paquetes en Function App:

Implementación con dependencias

  1. Instale todos los paquetes necesarios localmente mediante la ejecución de npm install.

  2. Implemente el código y asegúrese de que la carpeta node_modules se incluye en la implementación.

Uso de Kudu (solo Windows)

  1. Ir a https://<function_app_name>.scm.azurewebsites.net.

  2. Seleccione la Consola de depuración>CMD.

  3. Vaya a D:\home\site\wwwroot y luego arrastre el archivo package.json a la carpeta D:\home\site\wwwroot en la mitad superior de la página.
    También puede cargar archivos en Function App de otras formas. Para obtener más información, vea Actualización de los archivos de aplicación de función.

  4. Una vez cargado el archivo package.json, ejecute el comandonpm install en la npm install.
    Esta acción descarga los paquetes indicados en el archivo package.json y se reinicia Function App.

Variables de entorno

Agregue sus propias variables de entorno a una aplicación de funciones, en entornos locales y en la nube, como secretos operativos (cadenas de conexión, claves y puntos de conexión) o la configuración del entorno (como las variables de generación de perfiles). Acceda a esta configuración mediante process.env en el código de la función.

Entorno de desarrollo local

Cuando se ejecuta a nivel local, el proyecto de funciones incluye un archivo , donde se almacenan las variables de entorno en el objeto Values.

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

Entorno en la nube de Azure

Cuando se ejecuta en Azure, la aplicación de funciones permite establecer usos de la configuración de la aplicación, como cadenas de conexión de servicio, y expone estos valores como variables de entorno durante la ejecución.

Hay varias maneras de agregar, actualizar y eliminar opciones de configuración de la aplicación de función:

Para aplicar los cambios realizados en la configuración de la aplicación de funciones, es necesario reiniciar la aplicación de funciones.

Acceso a variables de entorno en el código

Acceda a la configuración de la aplicación como variables de entorno mediante process.env, como se muestra aquí en la segunda y tercera llamada a context.log() donde se registran las variables de entorno AzureWebJobsStorage y WEBSITE_SITE_NAME:

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

Módulos ECMAScript (versión preliminar)

Nota

Los módulos ECMAScript son actualmente una característica en versión preliminar en Node.js 14 y 16 (Azure Functions).

Los módulos ECMAScript (módulos ES) son el nuevo sistema de módulos estándar oficial de Node.js. Hasta ahora, en los ejemplos de código de este artículo se usa la sintaxis CommonJS. Al ejecutar Azure Functions en Node.js 14 o una versión posterior, puede optar por escribir las funciones mediante la sintaxis de los módulos ES.

Para usar los módulos ES en una función, cambie su nombre de archivo para usar una extensión .mjs. El siguiente archivo index.mjs es una función desencadenada por HTTP que usa la sintaxis de los módulos ES para importar la biblioteca y devolver un valor.

import { v4 as uuidv4 } from 'uuid';

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

Configuración del punto de entrada de la función

Las propiedades de function.jsonscriptFile y entryPoint pueden usarse para configurar la ubicación y el nombre de la función exportada. Estas propiedades pueden ser importantes si se transpila el código JavaScript.

Usar scriptFile

De forma predeterminada, se ejecuta una función de JavaScript desde index.js, un archivo que comparte el mismo directorio primario que su archivo function.json correspondiente.

scriptFile se puede usar para obtener una estructura de carpetas que tenga el aspecto del ejemplo siguiente:

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

function.json para myNodeFunction debe incluir una propiedad scriptFile que señala al archivo con la función exportada que ejecutar.

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

Usar entryPoint

En scriptFile (o index.js), una función debe exportarse con module.exports para que se pueda encontrar y ejecutar. De forma predeterminada, la función que se ejecuta cuando se desencadena es la única exportación de ese archivo, la exportación denominada run o la exportación denominada index.

Se puede configurar mediante entryPoint en function.json, como en el ejemplo siguiente:

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

En Functions 2.x o una versión posterior, que admite el parámetro this en funciones de usuario, el código de la función podría ser similar al del ejemplo siguiente:

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

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

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

En este ejemplo, es importante tener en cuenta que, aunque se exporta un objeto, no hay ninguna garantía de que se conserve el estado entre ejecuciones.

Depuración local

Cuando se inicia con el parámetro --inspect, un proceso de Node.js escucha un cliente de depuración en el puerto especificado. En Azure Functions 2.x o una versión posterior, puede especificar argumentos para pasar al proceso de Node.js que ejecuta el código mediante la adición de la variable de entorno o la configuración de la aplicación languageWorkers:node:arguments = <args>.

Para depurar de forma local, agregue "languageWorkers:node:arguments": "--inspect=5858" en Values en su archivo "languageWorkers:node:arguments": "--inspect=5858" y asocie un depurador al puerto 5858.

Al depurar con VS Code, el parámetro --inspect se agrega automáticamente mediante el valor port del archivo launch.json del proyecto.

En la versión 1.x, no funciona el valor de configuración languageWorkers:node:arguments. El puerto de depuración se puede seleccionar con el parámetro --nodeDebugPort en Azure Functions Core Tools.

Nota

Solo se puede configurar languageWorkers:node:arguments cuando se ejecuta la aplicación de función localmente.

Pruebas

Las pruebas de las funciones incluyen:

  • HTTP de un extremo a otro: para probar una función desde su punto de conexión HTTP, se puede usar cualquier herramienta que pueda realizar una solicitud HTTP como cURL, Postman o el método de recuperación de cambios de JavaScript.

  • Pruebas de integración: la prueba de integración incluye la capa de aplicación de función. Esta prueba significa que se debe controlar los parámetros en la función, incluida la solicitud y el contexto. El contexto es único para cada tipo de desencadenador y significa que debe conocer los enlaces entrantes y salientes para ese tipo de desencadenador.

    Obtenga más información sobre las pruebas de integración y la simulación de la capa de contexto con un repositorio GitHub experimental https://github.com/anthonychu/azure-functions-test-utils.

  • Prueba unitaria: las pruebas unitarias se realizan dentro de la aplicación de función. Se puede usar cualquier herramienta que pueda probar JavaScript, como Jest o Mocha.

TypeScript

Cuando el destino es la versión 2.x o posterior del sistema en tiempo de ejecución de Functions, tanto Azure Functions para Visual Studio Code como Azure Functions Core Tools le permiten crear aplicaciones de función mediante una plantilla que admita proyectos de aplicación de función de TypeScript. La plantilla genera archivos de proyecto package.json y tsconfig.json que facilitan la transpilación, la ejecución y la publicación de funciones de JavaScript desde el código de TypeScript con estas herramientas.

El archivo .funcignore generado se usa para indicar qué archivos se excluyen cuando se publica un proyecto en Azure.

Los archivos de TypeScript (TS) se transpilan en archivos de JavaScript (.js) en el directorio de salida dist. Las plantillas de TypeScript usan el parámetro en function.json para indicar la ubicación del archivo .js correspondiente en la carpeta dist. La ubicación de salida se establece mediante la plantilla con el parámetro outDir en el archivo tsconfig.json. Si cambia esta configuración o el nombre de la carpeta, el entorno de ejecución no podrá encontrar el código que se ejecutará.

La manera en que desarrolle e implemente de forma local a partir de un proyecto de TypeScript depende de la herramienta de desarrollo.

Visual Studio Code

La extensión Azure Functions para Visual Studio Code le permite desarrollar las funciones con TypeScript. Core Tools es un requisito de la extensión de Azure Functions.

Para crear una aplicación de función de TypeScript en Visual Studio Code, elija TypeScript como lenguaje al crear una aplicación de función.

Al presionar F5 para ejecutar la aplicación localmente, se realiza la transpilación antes de que se inicialice el host (func.exe).

Cuando se implementa la aplicación de función en Azure mediante el botón Deploy to function app... (Implementar en la aplicación de función), la extensión de Azure Functions genera primero una compilación lista para producción de los archivos JavaScript a partir de los archivos de origen de TypeScript.

Azure Functions Core Tools

Hay varias cuestiones en las que un proyecto de TypeScript difiere de un proyecto de JavaScript cuando se usa Core Tools.

Crear proyecto

Para crear un proyecto de aplicación de función de TypeScript con Core Tools, debe especificar la opción de lenguaje TypeScript al crear la aplicación de función. Para ello, siga uno de estos métodos:

  • Ejecute el comando func init, seleccione node como la pila de lenguaje y, luego, seleccione typescript.

  • Ejecute el comando func init --worker-runtime typescript.

Ejecución local

Para ejecutar el código de la aplicación de función localmente mediante Core Tools, use los comandos siguientes en lugar de func host start:

npm install
npm start

El comando npm start es equivalente a los siguientes comandos:

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

Publicación en Azure

Antes de usar el comando func azure functionapp publish para implementar en Azure, puede crear una compilación lista para producción de archivos de JavaScript a partir de los archivos de origen de TypeScript.

Los siguientes comandos preparan y publican el proyecto de TypeScript mediante Core Tools:

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

En este comando, reemplace <APP_NAME> por el nombre de la aplicación de función.

Consideraciones para las funciones de JavaScript

Cuando se trabaja con las funciones de JavaScript, tenga en cuenta las consideraciones de las secciones siguientes.

Elección de los planes de App Service de una sola vCPU

Al crear una aplicación de función que usa el plan de App Service, se recomienda que seleccione un plan de una sola vCPU, en lugar de un plan con varias vCPU. En la actualidad, Functions ejecuta funciones de JavaScript con más eficacia en VM con una sola vCPU; el uso de máquinas virtuales más grandes no produce las mejoras de rendimiento esperadas. Cuando sea necesario, puede escalar horizontalmente de forma manual mediante la adición de más instancias de máquina virtual de una sola vCPU o bien puede habilitar el escalado automático. Para obtener más información, consulte Escalación del recuento de instancias de forma manual o automática.

Arranque en frío

Al desarrollar Azure Functions en el modelo de hospedaje sin servidor, los arranques en frío son una realidad. Arranque en frío hace referencia al hecho de que cuando se inicia Function App por primera vez tras un período de inactividad, tarda más tiempo en iniciarse. Especialmente para las funciones de JavaScript con árboles de dependencias de gran tamaño, el arranque en frío puede ser importante. Para acelerar el proceso de arranque en frío, ejecute sus funciones como un archivo de paquete cuando sea posible. Muchos métodos de implementación utilizan el modelo de ejecución desde paquete de forma predeterminada, pero si experimenta arranques en frío prolongados y no ejecuta sus funciones de este modo, este cambio puede mejorar la situación considerablemente.

Límites de conexión

Cuando use un cliente específico del servicio en una aplicación de Azure Functions, no cree un cliente con cada invocación de función. En su lugar, cree un único cliente estático en el ámbito global. Para más información, consulte Administrar conexiones en Azure Functions.

Uso de async y await

Al escribir funciones de Azure en JavaScript, debe escribir el código mediante las palabras clave async y await. El hecho de escribir el código con async y await en lugar de usar devoluciones de llamada o .then y .catch con promesas ayuda a evitar dos problemas comunes:

  • El inicio de excepciones no detectadas que bloquean el proceso de Node.js, que puede afectar a la ejecución de otras funciones.
  • Un comportamiento inesperado, como la ausencia de registros de context.log debido a llamadas asincrónicas que no se esperan correctamente.

En el ejemplo siguiente, se invoca el método asincrónico fs.readFile con una función de devolución de llamada error-first como segundo parámetro. Este código provoca los dos problemas mencionados anteriormente. Una excepción no detectada explícitamente en el ámbito correcto bloquea todo el proceso (problema 1). Una llamada a la versión 1.x del método context.done() fuera del ámbito de la función de devolución de llamada significa que la invocación de la función puede finalizar antes de que se lea el archivo (problema 2). En este ejemplo, una llamada a la versión 1.x del método context.done() demasiado pronto provoca la ausencia de las entradas de registro que empiezan por 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();
}

El uso de las palabras clave async y await ayuda a evitar ambos errores. Debe usar la función de utilidad util.promisify de Node.js para convertir las funciones de estilo de devolución de llamada error-first en funciones que admiten await.

En el ejemplo siguiente, las excepciones no controladas iniciadas durante la ejecución de la función solo errarán la invocación individual que generó una excepción. La palabra clave await significa que los pasos después de readFileAsync solo se ejecutarán una vez finalizado readFile. Con async y await, tampoco es necesario llamar a la devolución de llamada 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}`);
}

Pasos siguientes

Para obtener más información, consulte los siguientes recursos: