Guía para desarrolladores de Node.js de Azure Functions
Esta guía es una introducción al desarrollo de Azure Functions con JavaScript o TypeScript. En este artículo se supone que ya ha leído la guía para desarrolladores de Azure Functions.
Importante
El contenido de este artículo cambia en función de la elección del modelo de programación de Node.js en el selector de la parte superior de esta página. La versión que elija debe coincidir con la versión del paquete npm @azure/functions
que está usando en la aplicación. Si no le aparece ese paquete en package.json
, el valor predeterminado es v3. Obtenga más información sobre las diferencias entre v3 y v4 en la guía de migración.
Como desarrollador de Node.js, puede que también le interese uno de los siguientes artículos:
Introducción | Conceptos | Aprendizaje guiado |
---|---|---|
Consideraciones
- El modelo de programación de Node.js no debe confundirse con el runtime de Azure Functions:
- Modelo de programación: define cómo crea el código y es específico de JavaScript y TypeScript.
- Runtime: define el comportamiento subyacente de Azure Functions y se comparte en todos los lenguajes.
- La versión del modelo de programación está estrictamente vinculada a la versión del paquete npm
@azure/functions
. Se versiona independientemente del runtime. Tanto el runtime como el modelo de programación usan el número 4 como versión principal más reciente, pero es casualidad. - No puede mezclar los modelos de programación de las versiones 3 y 4 en la misma aplicación de funciones. En cuanto registre una función de la versión 4 en la aplicación, se omiten las funciones de la versión 3 registradas en los archivos function.json.
Versiones compatibles
En la tabla siguiente se muestra cada versión del modelo de programación de Node.js junto con sus versiones compatibles del entorno de ejecución y la Node.js de Azure Functions.
Versión del modelo de programación | Nivel de soporte técnico | Versión del runtime de Functions | Versión del Node.js | Descripción |
---|---|---|---|---|
4.x | GA | 4.25+ | 20.x, 18.x | Admite una estructura de archivos flexible y un enfoque centrado en código para desencadenadores y enlaces. |
3.x | GA | 4.x | 20.x, 18.x, 16.x, 14.x | Requiere una estructura de archivos específica con los desencadenadores y enlaces declarados en un archivo "function.json" |
2.x | N/D | 3.x | 14.x, 12.x, 10.x | Llegó al final del soporte técnico el 13 de diciembre de 2022. Consulte Versiones de Functions para obtener más información. |
1.x | N/D | 2.x | 10.x, 8.x | Llegó al final del soporte técnico el 13 de diciembre de 2022. Consulte Versiones de Functions para obtener más información. |
Estructura de carpetas
La estructura de carpetas necesaria para un proyecto de JavaScript se parece al ejemplo siguiente:
<project_root>/
| - .vscode/
| - node_modules/
| - myFirstFunction/
| | - index.js
| | - function.json
| - mySecondFunction/
| | - index.js
| | - function.json
| - .funcignore
| - host.json
| - local.settings.json
| - package.json
La carpeta de proyecto principal, <project_root>, puede contener los siguientes archivos:
- .vscode/: (Opcional) Contiene la configuración almacenada de Visual Studio Code. Para más información, consulte Configuración de Visual Studio Code.
- myFirstFunction/function.json: contiene la configuración del desencadenador, las entradas y las salidas de la función. El nombre del directorio determina el nombre de la función.
- myFirstFunction/index.js: almacena el código de la función. Para cambiar esta ruta de acceso de archivo predeterminada, consulte Uso de scriptFile.
- .funcignore: (Opcional) declara los archivos que no deben publicarse en Azure. Normalmente, este archivo contiene .vscode/ para omitir la configuración del editor, test/ para omitir los casos de prueba y local.settings.json para evitar la publicación de la configuración de la aplicación local.
- host.json: contiene las opciones de configuración global que afectan a todas las funciones de una instancia de aplicación de funciones. Este archivo se publica en Azure. No todas las opciones se admiten cuando se ejecuta localmente. Para más información, consulte host.json.
- local.settings.json: se usa para almacenar las cadenas de conexión y la configuración de la aplicación cuando la ejecución se realiza a nivel local. Este archivo no se publica en Azure. Para más información, consulte local.settings.file.
- package.json: contiene opciones de configuración como una lista de dependencias de paquete, el punto de entrada principal y los scripts.
La estructura de carpetas recomendada para un proyecto de JavaScript en Azure Functions tiene la siguiente apariencia:
<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
La carpeta de proyecto principal, <project_root>, puede contener los siguientes archivos:
- .vscode/: (Opcional) Contiene la configuración almacenada de Visual Studio Code. Para más información, consulte Configuración de Visual Studio Code.
- src/functions/: la ubicación predeterminada para todas las funciones y sus desencadenadores y enlaces relacionados.
- test/: (opcional) contiene los casos de prueba de la aplicación de funciones.
- .funcignore: (Opcional) declara los archivos que no deben publicarse en Azure. Normalmente, este archivo contiene .vscode/ para omitir la configuración del editor, test/ para omitir los casos de prueba y local.settings.json para evitar la publicación de la configuración de la aplicación local.
- host.json: contiene las opciones de configuración global que afectan a todas las funciones de una instancia de aplicación de funciones. Este archivo se publica en Azure. No todas las opciones se admiten cuando se ejecuta localmente. Para más información, consulte host.json.
- local.settings.json: se usa para almacenar las cadenas de conexión y la configuración de la aplicación cuando la ejecución se realiza a nivel local. Este archivo no se publica en Azure. Para más información, consulte local.settings.file.
- package.json: contiene opciones de configuración como una lista de dependencias de paquete, el punto de entrada principal y los scripts.
Registro de una función
El modelo v3 registra una función según la existencia de dos archivos. En primer lugar, necesita un archivo function.json
ubicado en una carpeta un nivel por debajo de la raíz de la aplicación. En segundo lugar, necesitará un archivo JavaScript que exporte la función. De forma predeterminada, el modelo busca un archivo index.js
en la misma carpeta que function.json
. Si usa TypeScript, deberá usar la propiedad scriptFile
en function.json
para que apunte al archivo JavaScript compilado. Para personalizar la ubicación del archivo o el nombre de exportación de la función, consulte la configuración del punto de entrada de la función.
La función que exporta siempre debe declararse como async function
en el modelo v3. Puede exportar una función sincrónica, pero debe llamar a context.done()
para indicar que la función se ha completado, lo que está en desuso y no se recomienda.
La función recibe una invocación context
como primer argumento y las entradas como argumentos restantes.
El ejemplo siguiente es una función simple que registra que se ha desencadenado y responde con 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!' };
};
El modelo de programación carga las funciones en función del campo main
de package.json
. Puede establecer el campo main
en un único archivo o en varios archivos mediante un patrón global. La siguiente tabla muestra valores de ejemplo para el campo main
:
Ejemplo | Descripción |
---|---|
src/index.js |
Registrar funciones desde un único archivo raíz. |
src/functions/*.js |
Registre cada función desde su propio archivo. |
src/{index.js,functions/*.js} |
Combinación en la que se registra cada función desde su propio archivo, pero todavía tiene un archivo raíz para el código general de nivel de aplicación. |
Para registrar una función, debe importar el objeto app
desde el módulo npm @azure/functions
y llamar al método específico del tipo de desencadenador. El primer argumento al registrar una función es el nombre de la función. El segundo argumento es un objeto options
que especifica la configuración del desencadenador, el controlador y cualquier otra entrada o salida. En algunos casos en los que la configuración del desencadenador no sea necesaria, puede pasar el controlador directamente como segundo argumento en lugar de un objeto options
.
El registro de una función se puede realizar desde cualquier archivo del proyecto, siempre y cuando ese archivo se cargue (directa o indirectamente) en función del campo main
del archivo package.json
. La función debe registrarse en un ámbito global porque no se pueden registrar funciones una vez iniciadas las ejecuciones.
El ejemplo siguiente es una función simple que registra que se ha desencadenado y responde con 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!' };
}
});
Entradas y salidas
La función es necesaria para tener exactamente una entrada principal denominada desencadenador. También puede tener entradas o salidas secundarias. Las entradas y salidas se configuran en los archivos function.json
y también se conocen como enlaces.
Entradas
Las entradas son enlaces con direction
establecido en in
. La principal diferencia entre un desencadenador y una entrada secundaria es que type
para un desencadenador finaliza en Trigger
; por ejemplo, el tipo blobTrigger
en comparación con el tipo blob
. La mayoría de las funciones solo usan un desencadenador y no se admiten muchos tipos de entrada secundarios.
Se puede acceder a las entradas de varias maneras:
[Recomendado] Como argumentos pasados a la función: use los argumentos en el mismo orden en que se definen en
function.json
. La propiedadname
definida enfunction.json
no tiene que coincidir con el nombre del argumento, aunque se recomienda para la organización.module.exports = async function (context, myTrigger, myInput, myOtherInput) { ... };
Como propiedades de
context.bindings
: use la clave que coincide con la propiedadname
definida enfunction.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); };
Salidas
Las salidas son enlaces con direction
establecido en out
y se pueden establecer de varias maneras:
[Recomendado para una salida única] Devuelva el valor directamente: si usa una función asincrónica, puede devolver el valor directamente. Debe cambiar la propiedad
name
del enlace de salida a$return
enfunction.json
como en el ejemplo siguiente:{ "name": "$return", "type": "http", "direction": "out" }
module.exports = async function (context, request) { return { body: "Hello, world!" }; }
[Recomendado para varias salidas] Devuelva un objeto que contenga todas las salidas: si usa una función asincrónica, puede devolver un objeto con una propiedad que coincida con el nombre de cada enlace de
function.json
. En el ejemplo siguiente se usan enlaces de salida denominados "httpResponse" y "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 }; };
Establezca valores en
context.bindings
: si no usa una función asincrónica o no quiere usar las opciones anteriores, puede establecer los valores directamente encontext.bindings
, en los que la clave coincide con el nombre del enlace. En el ejemplo siguiente se usan enlaces de salida denominados "httpResponse" y "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; };
Tipo de datos de enlaces
Puede usar la propiedad dataType
en un enlace de entrada para cambiar el tipo de entrada, pero con algunas limitaciones:
- En Node.js, solo se admiten
string
ybinary
(stream
no). - En el caso de las entradas HTTP, se omite la propiedad
dataType
. En su lugar, use propiedades en el objetorequest
para obtener el cuerpo en el formato que quiera. Para obtener más información, consulte Solicitud HTTP.
En el ejemplo siguiente de un desencadenador de cola de almacenamiento, el tipo predeterminado de myQueueItem
es string
, pero si se establece dataType
en binary
, el tipo cambia a Buffer
de 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');
}
};
La función es necesaria para tener exactamente una entrada principal denominada desencadenador. También puede tener entradas secundarias, una salida principal denominada salida de retorno o salidas secundarias. Las entradas y salidas también se conocen como enlaces fuera del contexto del modelo de programación de Node.js. Antes de la versión v4 del modelo, estos enlaces se configuraron en archivos function.json
.
Entrada del desencadenador
El desencadenador es la única entrada o salida necesaria. Para la mayoría de los tipos de desencadenador, se registra una función mediante un método en el objeto app
denominado después del tipo de desencadenador. Puede especificar la configuración específica del desencadenador directamente en el argumento options
. Por ejemplo, un desencadenador HTTP permite especificar una ruta. Durante la ejecución, el valor correspondiente a este desencadenador se pasa como primer argumento al controlador.
const { app } = require('@azure/functions');
app.http('helloWorld1', {
route: 'hello/world',
handler: async (request, context) => {
...
}
});
Salida devuelta
La salida de devolución es opcional y, en algunos casos, está configurada de forma predeterminada. Por ejemplo, un desencadenador HTTP registrado con app.http
está configurado para devolver automáticamente una salida de respuesta HTTP. Para la mayoría de los tipos de salida, especifique la configuración de devolución en el argumento options
con la ayuda del objeto output
exportado desde el módulo @azure/functions
. Durante la ejecución, esta salida se establece devolviendo desde el controlador.
En el ejemplo siguiente se usa un desencadenador de temporizador y una salida de cola de almacenamiento:
const { app, output } = require('@azure/functions');
app.timer('timerTrigger1', {
schedule: '0 */5 * * * *',
return: output.storageQueue({
connection: 'storage_APPSETTING',
...
}),
handler: (myTimer, context) => {
return { hello: 'world' }
}
});
Entradas y salidas adicionales
Además del desencadenador y la devolución, es posible especificar entradas o salidas adicionales en el argumento options
al registrar una función. Los objetos input
y output
exportados desde el módulo @azure/functions
proporcionan métodos específicos del tipo para ayudar a construir la configuración. Durante la ejecución, obtiene o establece los valores con context.extraInputs.get
o context.extraOutputs.set
, pasando el objeto de configuración original como primer argumento.
El ejemplo siguiente es una función desencadenada por una cola de almacenamiento, con una entrada de blob de almacenamiento adicional que se copia en una salida de blob adicional. El mensaje de cola debe ser el nombre de un archivo y reemplaza {queueTrigger}
como nombre de blob que se va a copiar, con la ayuda de una expresión de enlace.
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);
}
});
Entradas y salidas genéricas
Los objetos app
, trigger
, input
y output
exportados por el módulo @azure/functions
proporcionan métodos específicos del tipo para la mayoría de los tipos. Para todos los tipos que no se admiten, se proporciona un método generic
para que pueda especificar manualmente la configuración. El método generic
también se puede usar si desea cambiar la configuración predeterminada proporcionada por un método específico del tipo.
El ejemplo siguiente es una función simple desencadenada por HTTP mediante métodos genéricos en lugar de métodos específicos del tipo.
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!` };
}
});
Contexto de invocación
Cada invocación de la función recibe un objeto de invocación context
, que se usa para leer entradas, establecer salidas, escribir en registros y leer varios metadatos. En el modelo v3, el objeto de contexto siempre es el primer argumento pasado al controlador.
El objeto context
tiene las siguientes propiedades:
Propiedad | Descripción |
---|---|
invocationId |
El identificador de la invocación de la función actual. |
executionContext |
Consulte Contexto de ejecución. |
bindings |
Consulte Enlaces. |
bindingData |
Metadatos sobre la entrada del desencadenador para esta invocación, sin incluir el propio valor. Por ejemplo, un desencadenador del centro de eventos tiene una propiedad enqueuedTimeUtc . |
traceContext |
Contexto para el seguimiento distribuido. Para obtener más información, vea Trace Context . |
bindingDefinitions |
Configuración de las entradas y salidas, tal como se define en function.json . |
req |
Consulte Solicitud HTTP. |
res |
Consulte Respuesta HTTP. |
context.executionContext
El objeto context.executionContext
tiene las siguientes propiedades:
Propiedad | Descripción |
---|---|
invocationId |
El identificador de la invocación de la función actual. |
functionName |
Nombre de la función invocada. EL nombre de la carpeta que contiene el archivo function.json determina el nombre de la función. |
functionDirectory |
Carpeta que contiene el archivo function.json . |
retryContext |
Consulte Contexto de reintento. |
context.executionContext.retryContext
El objeto context.executionContext.retryContext
tiene las siguientes propiedades:
Propiedad | Descripción |
---|---|
retryCount |
Número que representa el intento de reintento actual. |
maxRetryCount |
Número máximo de veces que se reintenta una ejecución. Un valor de -1 significa que se reintentará indefinidamente. |
exception |
Excepción que provocó el reintento. |
context.bindings
El objeto context.bindings
se usa para leer entradas o establecer salidas. El ejemplo siguiente es un desencadenador de cola de almacenamiento, que usa context.bindings
para copiar una entrada de blob de almacenamiento a una salida de blob de almacenamiento. El contenido del mensaje de la cola reemplaza {queueTrigger}
como nombre de archivo que se va a copiar, con la ayuda de una expresión de enlace.
{
"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
Este método context.done
está en desuso. Antes de que se admitieran las funciones asincrónicas, se indicaba que la función había terminado mediante una llamada a context.done()
:
module.exports = function (context, request) {
context.log("this pattern is now deprecated");
context.done();
};
Ahora, se recomienda quitar la llamada a context.done()
y marcar la función como asincrónica para que devuelva una promesa (incluso si no aplica await
a nada). Tan pronto como finalice la función (en otras palabras, se resuelva la promesa devuelta), el modelo v3 sabrá que la función ha terminado.
module.exports = async function (context, request) {
context.log("you don't need context.done or an awaited call")
};
Cada invocación de la función recibe un objeto context
de invocación, con información adicional sobre el contexto y los métodos usados para el registro. En el modelo v4, el objeto context
suele ser el segundo argumento pasado al controlador.
La clase InvocationContext
tiene las siguientes propiedades:
Propiedad | Descripción |
---|---|
invocationId |
El identificador de la invocación de la función actual. |
functionName |
El nombre de la función. |
extraInputs |
Se usa para obtener los valores de entradas adicionales. Para obtener más información, consulte Entradas y salidas adicionales. |
extraOutputs |
Se usa para establecer los valores de salidas adicionales. Para obtener más información, consulte Entradas y salidas adicionales. |
retryContext |
Consulte Contexto de reintento. |
traceContext |
Contexto para el seguimiento distribuido. Para obtener más información, vea Trace Context . |
triggerMetadata |
Metadatos sobre la entrada del desencadenador para esta invocación, sin incluir el propio valor. Por ejemplo, un desencadenador del centro de eventos tiene una propiedad enqueuedTimeUtc . |
options |
Las opciones usadas al registrar la función, después de que se hayan validado y con los valores predeterminados especificados explícitamente. |
Contexto de reintento
El objeto retryContext
tiene las siguientes propiedades:
Propiedad | Descripción |
---|---|
retryCount |
Número que representa el intento de reintento actual. |
maxRetryCount |
Número máximo de veces que se reintenta una ejecución. Un valor de -1 significa que se reintentará indefinidamente. |
exception |
Excepción que provocó el reintento. |
Para obtener más información, vea retry-policies
.
Registro
En Azure Functions, se recomienda usar context.log()
para escribir registros. Azure 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 registro de la aplicación y de las salidas del seguimiento. Para más información, consulte Supervisión de Azure Functions.
Nota:
Si usa el método alternativo console.log
de Node.js, se realiza un seguimiento de esos registros en el nivel de aplicación y no se asociará a ninguna función específica. Se recomienda encarecidamente usar context
para el registro, en lugar de console
, para que todos los registros estén asociados a una función específica.
En el ejemplo siguiente se escribe un registro en el nivel de "información" predeterminado, incluido el id. de invocación:
context.log(`Something has happened. Invocation ID: "${context.invocationId}"`);
Niveles de registro
Además del método context.log
predeterminado, están disponibles los siguientes métodos para permitirle escribir registros en niveles específicos:
Método | Descripción |
---|---|
context.log.error() |
Escribe un evento de nivel de error en los registros. |
context.log.warn() |
Escribe un evento de nivel de advertencia en los registros. |
context.log.info() |
Escribe un evento de nivel de información en los registros. |
context.log.verbose() |
Escribe un evento de nivel de seguimiento en los registros. |
Método | Descripción |
---|---|
context.trace() |
Escribe un evento de nivel de seguimiento en los registros. |
context.debug() |
Escribe un evento de nivel de depuración en los registros. |
context.info() |
Escribe un evento de nivel de información en los registros. |
context.warn() |
Escribe un evento de nivel de advertencia en los registros. |
context.error() |
Escribe un evento de nivel de error en los registros. |
Configurar el nivel de registro
Azure Functions permite definir el nivel de umbral que se usará al seguir y visualizar los registros. Para establecer el umbral, use la propiedad logging.logLevel
del archivo host.json
. Esta propiedad permite definir un nivel predeterminado aplicado a todas las funciones o un umbral para cada función individual. Para más información, consulte Configuración de la supervisión para Azure Functions.
Seguimiento de datos personalizados
De forma predeterminada, Azure 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 personalizados a la instancia de 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});
};
El parámetro tagOverrides
establece operation_Id
en el identificador de invocación de la función. Esta configuración le permite correlacionar todos los registros personalizados y generados automáticamente para una determinada invocación de función.
Desencadenadores HTTP
Los desencadenadores HTTP y de webhook usan objetos de solicitud y respuesta para representar mensajes HTTP.
Los desencadenadores HTTP y de webhook usan objetos HttpRequest
y HttpResponse
para representar mensajes HTTP. Las clases representan un subconjunto del estándar de captura, mediante el paquete undici
de Node.js.
Solicitud HTTP
Se puede acceder a una solicitud de varias maneras:
Como segundo argumento de la función:
module.exports = async function (context, request) { context.log(`Http function processed request for url "${request.url}"`);
Desde la propiedad
context.req
:module.exports = async function (context, request) { context.log(`Http function processed request for url "${context.req.url}"`);
Desde los enlaces de entrada con nombre: esta opción funciona igual que cualquier enlace no HTTP. El nombre del enlace de
function.json
debe coincidir con la clave encontext.bindings
, o "request1" en el ejemplo siguiente:{ "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}"`);
El objeto HttpRequest
tiene las siguientes propiedades:
Propiedad | Tipo | Descripción |
---|---|---|
method |
string |
Método de solicitud HTTP usado para invocar esta función. |
url |
string |
URL de la solicitud. |
headers |
Record<string, string> |
Encabezados de solicitud HTTP. Este objeto distingue entre mayúsculas y minúsculas. Se recomienda usar request.getHeader('header-name') en su lugar, que no distingue entre mayúsculas y minúsculas. |
query |
Record<string, string> |
Claves y valores de los parámetros de la cadena de consulta de la URL. |
params |
Record<string, string> |
Valores y claves de parámetros de ruta. |
user |
HttpRequestUser | null |
Objeto que representa al usuario que ha iniciado sesión, ya sea a través de la autenticación de Functions, la autenticación SWA o null cuando no se inicia sesión ningún usuario de este tipo. |
body |
Buffer | string | any |
Si el tipo de medio es "application/octet-stream" o "multipart/*", body es un búfer. Si el valor es una cadena que puede analizar como JSON, body es el objeto analizado. De lo contrario, body es una cadena. |
rawBody |
string |
Cuerpo como cadena. A pesar del nombre, esta propiedad no devuelve un búfer. |
bufferBody |
Buffer |
Cuerpo como búfer. |
Se puede acceder a la solicitud como primer argumento para el controlador de una función desencadenada por HTTP.
async (request, context) => {
context.log(`Http function processed request for url "${request.url}"`);
El objeto HttpRequest
tiene las siguientes propiedades:
Propiedad | Tipo | Descripción |
---|---|---|
method |
string |
Método de solicitud HTTP usado para invocar esta función. |
url |
string |
URL de la solicitud. |
headers |
Headers |
Encabezados de solicitud HTTP. |
query |
URLSearchParams |
Claves y valores de los parámetros de la cadena de consulta de la URL. |
params |
Record<string, string> |
Valores y claves de parámetros de ruta. |
user |
HttpRequestUser | null |
Objeto que representa al usuario que ha iniciado sesión, ya sea a través de la autenticación de Functions, la autenticación SWA o null cuando no se inicia sesión ningún usuario de este tipo. |
body |
ReadableStream | null |
Cuerpo como una secuencia legible. |
bodyUsed |
boolean |
Valor booleano que indica si el cuerpo ya se ha leído. |
Para acceder al cuerpo de una solicitud o respuesta, se pueden usar los métodos siguientes:
Método | Tipo de valor devuelto |
---|---|
arrayBuffer() |
Promise<ArrayBuffer> |
blob() |
Promise<Blob> |
formData() |
Promise<FormData> |
json() |
Promise<unknown> |
text() |
Promise<string> |
Nota:
Las funciones del cuerpo solo se pueden ejecutar una vez; Las llamadas posteriores se resolverán con cadenas vacías o ArrayBuffers.
Respuesta HTTP
La respuesta se puede establecer de varias maneras:
Establezca la propiedad
context.res
:module.exports = async function (context, request) { context.res = { body: `Hello, world!` };
Devuelva la respuesta: si la función es asincrónica y establece el nombre de enlace en
$return
enfunction.json
, puede devolver la respuesta directamente en lugar de establecerla encontext
.{ "type": "http", "direction": "out", "name": "$return" }
module.exports = async function (context, request) { return { body: `Hello, world!` };
Establezca el enlace de entrada con nombre: esta opción funciona igual que cualquier enlace no HTTP. El nombre del enlace de
function.json
debe coincidir con la clave encontext.bindings
, o "response1" en el ejemplo siguiente:{ "type": "http", "direction": "out", "name": "response1" }
module.exports = async function (context, request) { context.bindings.response1 = { body: `Hello, world!` };
Llame a
context.res.send()
: esta opción está en desuso. Llama implícitamente acontext.done()
y no se puede usar en una función asincrónica.module.exports = function (context, request) { context.res.send(`Hello, world!`);
Si crea un objeto nuevo al establecer la respuesta, dicho objeto debe coincidir con la interfaz HttpResponseSimple
, que tiene las siguientes propiedades:
Propiedad | Tipo | Descripción |
---|---|---|
headers |
Record<string, string> (opcional) |
Encabezados de respuesta HTTP. |
cookies |
Cookie[] (opcional) |
Cookies de respuesta HTTP. |
body |
any (opcional) |
Cuerpo de respuesta HTTP. |
statusCode |
number (opcional) |
Código de estado de respuesta HTTP. Si no se establece, el valor predeterminado es 200 . |
status |
number (opcional) |
Es igual que statusCode . Esta propiedad se ignora si statusCode está establecido. |
También puede modificar el objeto context.res
sin sobrescribirlo. El objeto predeterminado context.res
usa la interfaz HttpResponseFull
, que admite los métodos siguientes además de las propiedades HttpResponseSimple
:
Método | Descripción |
---|---|
status() |
Establece el estado. |
setHeader() |
Establece un campo de encabezado. NOTA: res.set() y res.header() también se admiten y hacen lo mismo. |
getHeader() |
Obtiene un campo de encabezado. NOTA: res.get() también se admite y hace lo mismo. |
removeHeader() |
Quita un encabezado. |
type() |
Establece el encabezado "content-type". |
send() |
Este método es desusado. Establece el cuerpo y llama a context.done() para indicar que ha terminado una función sincrónica. NOTA: res.end() también se admite y hace lo mismo. |
sendStatus() |
Este método es desusado. Establece el código de estado y llama a context.done() para indicar que ha terminado una función sincrónica. |
json() |
Este método es desusado. Establece el "content-type" en "application/json", establece el cuerpo y llama a context.done() para indicar que ha terminado una función sincrónica. |
La respuesta se puede establecer de varias maneras:
Como interfaz sencilla con el tipo
HttpResponseInit
: esta opción es la forma más concisa de devolver respuestas.return { body: `Hello, world!` };
La interfaz
HttpResponseInit
tiene las propiedades siguientes:Propiedad Tipo Descripción body
BodyInit
(opcional)Uno de los cuerpos de respuesta HTTP siguientes: ArrayBuffer
,AsyncIterable<Uint8Array>
,Blob
,FormData
,Iterable<Uint8Array>
,NodeJS.ArrayBufferView
,URLSearchParams
,null
ostring
.jsonBody
any
(opcional)Un cuerpo de respuesta HTTP serializable en JSON. Si se establece, la propiedad HttpResponseInit.body
se omite en favor de esta propiedad.status
number
(opcional)Código de estado de respuesta HTTP. Si no se establece, el valor predeterminado es 200
.headers
HeadersInit
(opcional)Encabezados de respuesta HTTP. cookies
Cookie[]
(opcional)Cookies de respuesta HTTP. Como clase con tipo
HttpResponse
: esta opción proporciona métodos auxiliares para leer y modificar varias partes de la respuesta, como los encabezados.const response = new HttpResponse({ body: `Hello, world!` }); response.headers.set('content-type', 'application/json'); return response;
La clase
HttpResponse
acepta un opcionalHttpResponseInit
como argumento para su constructor y tiene las siguientes propiedades:Propiedad Tipo Descripción status
number
Código de estado de respuesta HTTP. headers
Headers
Encabezados de respuesta HTTP. cookies
Cookie[]
Cookies de respuesta HTTP. body
ReadableStream | null
Cuerpo como una secuencia legible. bodyUsed
boolean
Valor booleano que indica si el cuerpo ya se ha leído.
Flujos HTTP
Los flujos HTTP son una característica que facilita el procesamiento de datos grandes, transmitir respuestas de OpenAI, ofrecer contenido dinámico y admitir otros escenarios HTTP principales. Permite transmitir solicitudes y respuestas desde puntos de conexión HTTP en la aplicación de funciones de Node.js. Use flujos HTTP en escenarios en los que la aplicación requiera intercambio en tiempo real e interacción entre el cliente y el servidor a través de HTTP. También puede usar secuencias HTTP para obtener el mejor rendimiento y confiabilidad para las aplicaciones cuando se usa HTTP.
Importante
Las secuencias HTTP no se admiten en el modelo v3. Actualice al modelo v4 para usar la característica de streaming HTTP.
Los tipos HttpRequest
y HttpResponse
existentes del modelo de programación v4 ya admiten varias formas de controlar el cuerpo del mensaje, incluso como una secuencia.
Requisitos previos
- El paquete npm
@azure/functions
versión 4.3.0 o posterior. - Runtime de Azure Functions versión 4.28 o posterior.
- Azure Functions Core Tools versión 4.0.5530 o una versión posterior, que contiene la versión en tiempo de ejecución correcta.
Habilitación de secuencias
Siga estos pasos para habilitar flujos HTTP en la aplicación de funciones en Azure y en los proyectos locales:
Si planea transmitir grandes cantidades de datos, modifique la configuración
FUNCTIONS_REQUEST_BODY_SIZE_LIMIT
en Azure. El tamaño máximo de cuerpo predeterminado permitido es104857600
, que limita las solicitudes a un tamaño de ~100 MB.Para el desarrollo local, agregue también
FUNCTIONS_REQUEST_BODY_SIZE_LIMIT
al archivo local.settings.json.Agregue el código siguiente a la aplicación en cualquier archivo incluido en el campo principal.
const { app } = require('@azure/functions'); app.setup({ enableHttpStream: true });
Ejemplos de secuencias
En este ejemplo se muestra una función desencadenada por HTTP que recibe datos a través de una solicitud HTTP POST y la función transmite estos datos a un archivo de salida especificado:
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!' };
},
});
En este ejemplo se muestra una función desencadenada por HTTP que transmite el contenido de un archivo como respuesta a las solicitudes 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 };
},
});
Para obtener una aplicación de ejemplo lista para ejecutar mediante secuencias, consulte este ejemplo en GitHub.
Consideraciones sobre secuencias
- Use
request.body
para obtener la ventaja máxima del uso de secuencias. Todavía puede seguir usando métodos comorequest.text()
, que siempre devuelven el cuerpo como una cadena.
Enlaces
Los enlaces no se admiten en el modelo v3. Actualice al modelo v4 para usar enlaces.
Use un enlace para ejecutar código en distintos puntos del ciclo de vida de Azure Functions. Los enlaces se ejecutan en el orden en que están registrados y se pueden registrar desde cualquier archivo de la aplicación. Actualmente hay dos ámbitos de enlaces, nivel "aplicación" e "invocación".
Enlaces de invocación
Los enlaces de invocación se ejecutan una vez por cada invocación de su función, ya sea antes en un enlace preInvocation
o después en un enlace postInvocation
. De manera predeterminada, su enlace se ejecuta para todos los tipos de desencadenador, pero también puede filtrar por tipo. En el ejemplo siguiente se muestra cómo registrar un enlace de invocación y filtrar por tipo de desencadenador:
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}`
);
}
});
El primer argumento para el controlador de enlace es un objeto de contexto específico de ese tipo de enlace.
El objeto PreInvocationContext
tiene las siguientes propiedades:
Propiedad | Descripción |
---|---|
inputs |
Argumentos pasados a la invocación. |
functionHandler |
Controlador de función para la invocación. Los cambios en este valor afectan a la propia función. |
invocationContext |
Objeto de contexto de invocación que se pasa a la función. |
hookData |
Lugar recomendado para almacenar y compartir datos entre enlaces en el mismo ámbito. Debe usar un nombre de propiedad único para que no entre en conflicto con los datos de otros enlaces. |
El objeto PostInvocationContext
tiene las siguientes propiedades:
Propiedad | Descripción |
---|---|
inputs |
Argumentos pasados a la invocación. |
result |
Resultado de la función. Los cambios en este valor afectan al resultado general de la función. |
error |
Error lanzado por la función, o null/indefinido si no hay error. Los cambios en este valor afectan al resultado general de la función. |
invocationContext |
Objeto de contexto de invocación que se pasa a la función. |
hookData |
Lugar recomendado para almacenar y compartir datos entre enlaces en el mismo ámbito. Debe usar un nombre de propiedad único para que no entre en conflicto con los datos de otros enlaces. |
Enlaces de aplicación
Los enlaces de aplicación se ejecutan una vez por cada instancia de su aplicación, ya sea durante el inicio en un enlace de appStart
o durante la finalización en un enlace de appTerminate
. Los enlaces de finalización de aplicación tienen un tiempo limitado para ejecutarse y no se ejecutan en todos los escenarios.
El entorno de ejecución de Azure Functions actualmente no admite registro de contexto fuera de una invocación. Use el paquete npm de Application Insights para registrar los datos durante los enlaces a nivel de aplicación.
En el ejemplo siguiente se registran enlaces de aplicación:
const { app } = require('@azure/functions');
app.hook.appStart((context) => {
// add your logic here
});
app.hook.appTerminate((context) => {
// add your logic here
});
El primer argumento para el controlador de enlace es un objeto de contexto específico de ese tipo de enlace.
El objeto AppStartContext
tiene las siguientes propiedades:
Propiedad | Descripción |
---|---|
hookData |
Lugar recomendado para almacenar y compartir datos entre enlaces en el mismo ámbito. Debe usar un nombre de propiedad único para que no entre en conflicto con los datos de otros enlaces. |
El objeto AppTerminateContext
tiene las siguientes propiedades:
Propiedad | Descripción |
---|---|
hookData |
Lugar recomendado para almacenar y compartir datos entre enlaces en el mismo ámbito. Debe usar un nombre de propiedad único para que no entre en conflicto con los datos de otros enlaces. |
Escalado y simultaneidad
De forma predeterminada, Azure Functions supervisa automáticamente la carga de su aplicación y crea más instancias de host para Node.js según sea necesario. Azure Functions usa umbrales integrados (no configurables por el usuario) en diferentes tipos de desencadenadores para decidir cuándo se deberán agregar instancias, como la antigüedad de los mensajes y el tamaño de la cola de 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. Puede aumentar el número de procesos de trabajo por host del valor predeterminado de 1 a un máximo de 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. Este comportamiento hará que sea menos probable que una función que consuma mucha CPU bloquee la ejecución de otras funciones. La configuración se aplica a cada host que Azure Functions crea al escalar horizontalmente la aplicación para satisfacer la demanda.
Advertencia
Use la configuración FUNCTIONS_WORKER_PROCESS_COUNT
con precaución. Ejecutar varios procesos en la misma instancia puede provocar un comportamiento impredecible y aumentar los tiempos de carga de la función. Si usa esta configuración, se recomienda encarecidamente compensar estas desventajas mediante la ejecución desde un archivo de paquete.
Versión de Node
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. Consulte supported versions
para obtener una lista de Node.js versiones compatibles con cada modelo de programación.
Especificación de la versión de Node
La forma en que actualice la versión de Node.js depende del sistema operativo en el que se ejecuta la aplicación de funciones.
Cuando se ejecuta en Windows, la versión de Node.js se establece mediante la configuración de la aplicación WEBSITE_NODE_DEFAULT_VERSION
. Esta configuración se puede actualizar mediante la CLI de Azure o en el Azure Portal.
Para saber más sobre las versiones de Node.js, vea las versiones compatibles.
Antes de actualizar su versión de Node.js, asegúrese de que la aplicación de funciones se ejecuta en la versión más reciente del entorno de ejecución de Azure Functions. Si necesita actualizar su versión del entorno de ejecución, consulte Migración de las aplicaciones de la versión 3.x de Azure Functions a la versión 4.x.
Ejecute el comando az functionapp config appsettings set
de la CLI de Azure para actualizar su versión de Node.js de la aplicación de funciones que se ejecuta en Windows:
az functionapp config appsettings set --settings WEBSITE_NODE_DEFAULT_VERSION=~20 \
--name <FUNCTION_APP_NAME> --resource-group <RESOURCE_GROUP_NAME>
Esto establece la configuración de la WEBSITE_NODE_DEFAULT_VERSION
aplicación de la versión de LTS admitida de ~20
.
Una vez realizados los cambios, la aplicación de funciones se reinicia. Para saber más sobre la compatibilidad de Node.js con Functions, consulte Directiva de compatibilidad de Language Runtime.
Variables de entorno
Las variables de entorno pueden ser útiles para los secretos operativos (cadenas de conexión, claves, puntos de conexión, etc.) o para la configuración del entorno, como las variables de generación de perfiles. Puede agregar variables de entorno en los entornos locales y en la nube y acceder a ellas a través de process.env
en el código de la función.
En el ejemplo siguiente se registra la variable de entorno 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"]}`);
}
Entorno de desarrollo local
Cuando se ejecute a nivel local, el proyecto de funciones incluirá un local.settings.json
archivo, donde se almacenarán las variables de entorno en el objeto Values
.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "node",
"CUSTOM_ENV_VAR_1": "hello",
"CUSTOM_ENV_VAR_2": "world"
}
}
Entorno en la nube de Azure
Cuando se ejecute en Azure, la aplicación de funciones permitirá establecer usos de la configuración de la aplicación, como cadenas de conexión de servicio, y expondrá 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.
Variables de entorno de trabajo
Hay varias variables de entorno de Functions específicas de Node.js:
languageWorkers__node__arguments
Esta configuración permite especificar argumentos personalizados al iniciar el proceso de Node.js. A menudo se usa localmente para iniciar el trabajo en modo de depuración, pero también se puede usar en Azure si necesita argumentos personalizados.
Advertencia
Si es posible, evite el uso de languageWorkers__node__arguments
en Azure porque puede tener un efecto negativo en el tiempo de inicio en frío. En lugar de usar trabajos iniciados previamente, el tiempo de ejecución debe iniciar un nuevo trabajo desde cero con los argumentos personalizados.
logging__logLevel__Worker
Esta configuración ajusta el nivel de registro predeterminado para los registros de trabajo específicos de Node.js. De forma predeterminada, solo se muestran los registros de advertencias o errores, pero puede establecerla en information
o debug
para ayudar a diagnosticar problemas con el trabajo de Node.js. Para más información, consulte Configuración de los niveles de registro.
Módulos ECMAScript (versión preliminar)
Nota
Los módulos ECMAScript son actualmente una característica en vista previa en Node.js 14 y siguientes en 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 uuid
y devolver un valor.
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
});
Configuración del punto de entrada de la función
Las propiedades de function.json
scriptFile
y entryPoint
pueden usarse para configurar la ubicación y el nombre de la función exportada. La propiedad scriptFile
es necesaria cuando se usa TypeScript y se debe apuntar al JavaScript compilado.
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:
<project_root>/
| - node_modules/
| - myFirstFunction/
| | - function.json
| - lib/
| | - sayHello.js
| - host.json
| - package.json
function.json
para myFirstFunction
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 el modelo v3, 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
. El ejemplo siguiente establece entryPoint
en function.json
un valor personalizado, "logHello":
{
"entryPoint": "logHello",
"bindings": [
...
]
}
async function logHello(context) {
context.log('Hello, world!');
}
module.exports = { logHello };
Depuración local
Se recomienda usar VS Code para la depuración local, que inicia automáticamente el proceso de Node.js en modo de depuración y se adjunta automáticamente al proceso. Para obtener más información, consulte Ejecución local de la función.
Si usa una herramienta diferente para la depuración o quiere iniciar manualmente el proceso de Node.js en modo de depuración, agregue "languageWorkers__node__arguments": "--inspect"
después de Values
en local.settings.json. El argumento --inspect
indica a Node.js que escuche un cliente de depuración, en el puerto 9229 de forma predeterminada. Para obtener más información, consulte la guía de depuración de Node.js.
Recomendaciones
En esta sección se describen varios patrones impactantes para aplicaciones de Node.js que recomendamos seguir.
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 Node.js con más eficacia en VM con una sola vCPU. El uso de máquinas virtuales más grandes no producirá 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.
Ejecución desde un archivo de paquete
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 a la primera vez en que su aplicación de funciones se inicia después de un período de inactividad, tardando más tiempo en iniciarse. Especialmente para las aplicaciones de Node.js 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 usan este modelo de forma predeterminada, pero si experimentase numerosos arranques en frío, debería hacer alguna comprobación para asegurarse de que se esté ejecutando de esta manera.
Uso de un único cliente estático
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, ya que puede alcanzar los límites de conexiones. 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 Node.js, deberá 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.
- 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 puede bloquear todo el proceso (problema 1). Devolver sin asegurarse de que la devolución de llamada ha finalizado significa que la respuesta http a veces tendrá un cuerpo vacío (incidencia 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 };
},
});
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 puede bloquear todo el proceso (problema 1). Llamar al método en desuso context.done()
fuera del ámbito de la devolución de llamada puede indicar que la función ha terminado antes de que se lea el archivo (problema 2). En este ejemplo, una llamada a 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();
}
Use las palabras clave async
y await
para ayudar a evitar ambos problemas. La mayoría de las API del ecosistema de Node.js se han convertido para admitir promesas de alguna forma. Por ejemplo, a partir de la versión 14, Node.js proporciona una API fs/promises
para reemplazar la API de devolución de llamada fs
.
En el ejemplo siguiente, las excepciones no controladas iniciadas durante la ejecución de la función solo devolverán un error para la invocación individual que generó una excepción. La palabra clave await
significa que los pasos después de readFile
solo se ejecutarán una vez finalizado.
// 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;
}
},
});
Con async
y await
, tampoco es necesario llamar a la devolución de llamada context.done()
.
// 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}`);
}
Solución de problemas
Consulte la guía de solución de problemas de Node.js.
Pasos siguientes
Para obtener más información, consulte los siguientes recursos: