Guia do desenvolvedor de Node.js do Azure Functions
Este guia é uma introdução ao desenvolvimento do Azure Functions usando o JavaScript ou TypeScript. O artigo assume que você já leu o Guia do desenvolvedor do Azure Functions.
Importante
O conteúdo deste artigo é alterado com base em sua escolha do modelo de programação Node.js no seletor na parte superior desta página. A versão escolhida deve corresponder à versão do pacote npm @azure/functions
que você está usando em seu aplicativo. Se você não tiver esse pacote listado em seu package.json
, o padrão será v3. Saiba mais sobre as diferenças entre v3 e v4 na guia de migração.
Como um desenvolvedor de Node.js, você também pode estar interessado em um dos seguintes artigos:
Introdução | Conceitos | Aprendizagem orientada |
---|---|---|
Considerações
- O modelo de programação Node.js não deve ser confundido com o runtime do Azure Functions:
- Modelo de programação: define como você cria seu código e é específico para JavaScript e TypeScript.
- Runtime: define o comportamento subjacente do Azure Functions e é compartilhado em todos os idiomas.
- A versão do modelo de programação está estritamente vinculada à versão do pacote npm
@azure/functions
. Ele tem controle de versão independentemente do runtime. O runtime e o modelo de programação usam o número 4 como sua versão principal mais recente, mas isso é uma coincidência. - Você não pode misturar os modelos de programação v3 e v4 no mesmo aplicativo de funções. Assim que você registra uma função v4 em seu aplicativo, todas as funções v3 registradas em arquivos function.json são ignoradas.
Versões com suporte
A tabela a seguir mostra cada versão do modelo de programação Node.js juntamente com suas versões com suporte do Azure Functions Runtime e Node.js.
Versão do Modelo de Programação | Nível de Suporte | Versão do Functions Runtime | Versão do Node.js | Descrição |
---|---|---|---|---|
4.x | GA | 4.25+ | 20.x, 18.x | Dá suporte a uma estrutura de arquivos flexível e uma abordagem centrada em código para gatilhos e associações. |
3.x | GA | 4.x | 20.x, 18.x, 16.x, 14.x | Requer uma estrutura de arquivo específica com seus gatilhos e associações declarados em um arquivo “function.json” |
2. x | N/D | 3.x | 14.x, 12.x, 10.x | Chegou ao fim do suporte em 13 de dezembro de 2022. Veja Versões do Functions para obter mais informações. |
1.x | N/D | 2. x | 10.x, 8.x | Chegou ao fim do suporte em 13 de dezembro de 2022. Veja Versões do Functions para obter mais informações. |
Estrutura de pastas
A estrutura de pastas necessária para um projeto JavaScript se parece com o seguinte exemplo:
<project_root>/
| - .vscode/
| - node_modules/
| - myFirstFunction/
| | - index.js
| | - function.json
| - mySecondFunction/
| | - index.js
| | - function.json
| - .funcignore
| - host.json
| - local.settings.json
| - package.json
A principal pasta do projeto <project_root> pode conter os seguintes arquivos:
- .vscode/: (opcional) contém a configuração do Visual Studio Code armazenadas. Para saber mais, confira Configurações de Visual Studio Code.
- myFirstFunction/function.json: contém a configuração do gatilho, das entradas e das saídas da função. O nome do diretório determina o nome da sua função.
- myFirstFunction/index.js: armazena seu código de função. Para alterar esse caminho de arquivo padrão, confira usando scriptFile.
- .funcignore: (opcional) declara os arquivos que não devem ser publicados no Azure. Normalmente, esse arquivo contém .vscode/ para ignorar a configuração do editor, test/ para ignorar os casos de teste e local.settings.json para impedir a publicação das configurações do aplicativo local.
- host.json: contém opções de configuração que afetam todas as funções em uma instância do aplicativo de funções. Esse arquivo é publicado do Azure. Nem todas as opções têm suporte quando executadas localmente. Para saber mais, confira host.json.
- local.settings.json: usado para armazenar as configurações do aplicativo e as cadeias de conexão ao ser executado localmente. Esse arquivo não é publicado no Azure. Para saber mais, confira local.settings.file.
- package.json: contém opções de configuração como uma lista de dependências de pacote, o ponto de entrada principal e scripts.
A estrutura de pastas recomendada para um projeto JavaScript é semelhante ao exemplo a seguir:
<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
A principal pasta do projeto <project_root> pode conter os seguintes arquivos:
- .vscode/: (opcional) contém a configuração do Visual Studio Code armazenadas. Para saber mais, confira Configurações de Visual Studio Code.
- src/functions/: o local padrão de todas as funções e seus respectivos gatilhos e associações.
- test/: (opcional) contém os casos de teste do aplicativo de funções.
- .funcignore: (opcional) declara os arquivos que não devem ser publicados no Azure. Normalmente, esse arquivo contém .vscode/ para ignorar a configuração do editor, test/ para ignorar os casos de teste e local.settings.json para impedir a publicação das configurações do aplicativo local.
- host.json: contém opções de configuração que afetam todas as funções em uma instância do aplicativo de funções. Esse arquivo é publicado do Azure. Nem todas as opções têm suporte quando executadas localmente. Para saber mais, confira host.json.
- local.settings.json: usado para armazenar as configurações do aplicativo e as cadeias de conexão ao ser executado localmente. Esse arquivo não é publicado no Azure. Para saber mais, confira local.settings.file.
- package.json: contém opções de configuração como uma lista de dependências de pacote, o ponto de entrada principal e scripts.
Registrando uma função
O modelo v3 registra uma função com base na existência de dois arquivos. Primeiro, você precisa de um arquivo function.json
localizado em uma pasta um nível abaixo da raiz do seu aplicativo. Em segundo lugar, você precisa de um arquivo JavaScript que exporte sua função. Por padrão, o modelo procura um arquivo index.js
na mesma pasta que seu function.json
. Se você estiver usando o TypeScript, deverá usar a propriedade scriptFile
em function.json
para apontar para o arquivo JavaScript compilado. Para personalizar a localização do arquivo ou o nome de exportação da sua função, consulte configurando o ponto de entrada da sua função.
A função que você exporta sempre deve ser declarada como async function
no modelo v3. Você pode exportar uma função síncrona, mas deve chamar context.done()
para sinalizar que sua função foi concluída, o que foi preterido e não é recomendado.
Sua função recebe uma invocação context
como o primeiro argumento e suas entradas como os argumentos restantes.
O exemplo a seguir é uma função simples que registra que ela foi disparada e responde com 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!' };
};
O modelo de programação carrega suas funções com base no campo main
em seu package.json
. Você pode definir o campo main
para um único ou vários arquivos usando um padrão glob. A tabela a seguir mostra valores de exemplo para o campo main
:
Exemplo | Descrição |
---|---|
src/index.js |
Registrar funções de um único arquivo raiz. |
src/functions/*.js |
Registrar cada função de seu próprio arquivo. |
src/{index.js,functions/*.js} |
Uma combinação em que você registra cada função de seu próprio arquivo, mas ainda tem um arquivo raiz para código geral no nível do aplicativo. |
Para registrar uma função, você deve importar o objeto app
do módulo npm @azure/functions
e chamar o método específico para o tipo de gatilho. O primeiro argumento ao registrar uma função é o nome da função. O segundo argumento é um objeto options
que especifica a configuração para o gatilho, o manipulador e outras entradas ou saídas. Em alguns casos onde a configuração do gatilho não é necessária, você pode passar o manipulador diretamente como o segundo argumento em vez de um objeto options
.
O registro de uma função pode ser feito a partir de qualquer arquivo em seu projeto, desde que esse arquivo seja carregado (direta ou indiretamente) com base no campo main
em seu arquivo package.json
. A função deve ser registrada em um escopo global porque não é possível registrar funções depois que as execuções são iniciadas.
O exemplo a seguir é uma função simples que registra que ela foi disparada e responde com 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 e saídas
Sua função precisa ter exatamente uma entrada primária, chamada gatilho. Também pode ter entradas e/ou saídas secundárias. As entradas e saídas estão configuradas em seus arquivos function.json
e também são referidas como associações.
Entradas
As entradas são associações com direction
definido como in
. A principal diferença entre um gatilho e uma entrada secundária é que o type
de um gatilho termina em Trigger
, por exemplo, tipo blobTrigger
versus tipo blob
. A maioria das funções usa apenas um gatilho e não há suporte para muitos tipos de entradas secundárias.
As entradas podem ser acessadas de várias maneiras:
[Recomendado]Como argumentos passados para sua função: Use os argumentos na mesma ordem em que são definidos em
function.json
. A propriedadename
definida emfunction.json
não precisa corresponder ao nome do seu argumento, embora seja recomendada por uma questão de organização.module.exports = async function (context, myTrigger, myInput, myOtherInput) { ... };
Como propriedades de
context.bindings
: use a chave correspondente à propriedadename
definida emfunction.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); };
Saídas
As saídas são associações com direction
definidas como out
, e podem ser definidas de várias maneiras:
[Recomendado para saída única] Retornar o valor diretamente: se você estiver usando uma função assíncrona, poderá retornar o valor diretamente. Você deve alterar a propriedade
name
da associação de saída para$return
emfunction.json
, como no exemplo a seguir:{ "name": "$return", "type": "http", "direction": "out" }
module.exports = async function (context, request) { return { body: "Hello, world!" }; }
[Recomendado para várias saídas] Retorne um objeto contendo todas as saídas: Se você estiver usando uma função assíncrona, poderá retornar um objeto com uma propriedade que corresponda ao nome de cada associação no seu
function.json
. O exemplo a seguir usa associações de saída denominadas "httpResponse" e "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 }; };
Defina valores em
context.bindings
: Se você não estiver usando uma função assíncrona ou não quiser usar as opções anteriores, poderá definir os valores diretamente emcontext.bindings
, em que a chave corresponde ao nome da associação. O exemplo a seguir usa associações de saída denominadas "httpResponse" e "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 dados de associações
Você pode usar a propriedade dataType
em uma associação de entrada para alterar o tipo da sua entrada, no entanto, ela tem algumas limitações:
- No Node.js, apenas
string
ebinary
são suportados (stream
não é) - Para entradas HTTP, a propriedade
dataType
é ignorada. Em vez disso, use propriedades no objetorequest
para obter o corpo no formato desejado. Para obter mais informações, consulte Solicitação HTTP.
No exemplo a seguir de um gatilho de fila de armazenamento , o tipo padrão de myQueueItem
é um string
, mas se você definir dataType
para binary
, o tipo será alterado para um Node.js Buffer
.
{
"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');
}
};
Sua função precisa ter exatamente uma entrada primária, chamada gatilho. Ela também pode ter entradas secundárias, uma saída primária chamada saída de retorno e/ou saídas secundárias. As entradas e saídas também são chamadas de associações fora do contexto do modelo de programação do Node.js. Antes da v4 do modelo, essas associações eram configuradas em arquivos function.json
.
Entrada de gatilho
O gatilho é a única entrada ou saída necessária. Para a maioria dos tipos de gatilho, registre uma função usando um método no objeto app
com o nome do tipo de gatilho. Você pode especificar a configuração específica para o gatilho diretamente no argumento options
. Por exemplo, um gatilho HTTP permite que você especifique uma rota. Durante a execução, o valor correspondente a esse gatilho é passado como o primeiro argumento para o manipulador.
const { app } = require('@azure/functions');
app.http('helloWorld1', {
route: 'hello/world',
handler: async (request, context) => {
...
}
});
Saída de retorno
A saída de retorno é opcional e, em alguns casos, configurada por padrão. Por exemplo, um gatilho HTTP registrado com app.http
é configurado para retornar uma saída de resposta HTTP automaticamente. Para a maioria dos tipos de saída, especifique a configuração de retorno no argumento options
com a ajuda do objeto output
exportado do módulo @azure/functions
. Durante a execução, você define essa saída retornando-a do manipulador.
O exemplo a seguir usa um gatilho de temporizador e uma saída de fila de armazenamento :
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 e saídas extras
Além do gatilho e do retorno, você pode especificar entradas ou saídas extras no argumento options
ao registrar uma função. Os objetos input
e output
exportados do módulo @azure/functions
fornecem métodos de tipo específicos para ajudar a construir a configuração. Durante a execução, você obtém ou define os valores com context.extraInputs.get
ou context.extraOutputs.set
, passando o objeto de configuração original como o primeiro argumento.
O exemplo a seguir é uma função acionada por uma fila de armazenamento, com uma entrada de blob de armazenamento extra copiada para uma saída de blob de armazenamento extra. A mensagem da fila deve ser o nome de um arquivo e substitui {queueTrigger}
como o nome do blob a ser copiado, com a ajuda de uma expressão de associação.
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 e saídas genéricas
Os objetos app
, trigger
, input
e output
exportados pelo módulo @azure/functions
fornecem métodos de tipo específicos para a maioria dos tipos. Para todos os tipos sem suporte, um método generic
foi fornecido para permitir que você especifique a configuração manualmente. O método generic
também pode ser usado se você quiser alterar as configurações padrão fornecidas por um método de tipo específico.
O exemplo a seguir é uma função acionada por HTTP simples usando métodos genéricos em vez de métodos específicos de 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 invocação
Cada invocação de sua função recebe um objeto de invocação context
, usado para ler entradas, definir saídas, gravar em logs e ler vários metadados. No modelo v3, o objeto de contexto é sempre o primeiro argumento passado para seu manipulador.
O objeto context
tem as seguintes propriedades:
Propriedade | Descrição |
---|---|
invocationId |
A ID da invocação de função atual. |
executionContext |
Consulte o contexto de execução. |
bindings |
Consulte as associações. |
bindingData |
Metadados sobre a entrada do gatilho para essa invocação, sem incluir o valor em si. Por exemplo, um gatilho do hub de eventos tem uma propriedade enqueuedTimeUtc . |
traceContext |
O contexto para rastreamento distribuído. Para obter mais informações, consulte Trace Context . |
bindingDefinitions |
A configuração das suas entradas e saídas, conforme definido em function.json . |
req |
Consulte Solicitação HTTP . |
res |
Consulte Resposta HTTP . |
context.executionContext
O objeto context.executionContext
tem as seguintes propriedades:
Propriedade | Descrição |
---|---|
invocationId |
A ID da invocação de função atual. |
functionName |
O nome da função que está sendo invocada. O nome da pasta que contém o arquivo function.json determina o nome da função. |
functionDirectory |
A pasta que contém o arquivo function.json . |
retryContext |
Consulte o contexto da nova tentativa. |
context.executionContext.retryContext
O objeto context.executionContext.retryContext
tem as seguintes propriedades:
Propriedade | Descrição |
---|---|
retryCount |
Um número que representa a tentativa da nova tentativa atual. |
maxRetryCount |
O número máximo de vezes que uma execução foi repetida. Um valor de -1 significa tentar indefinidamente. |
exception |
Exceção que causou a nova tentativa. |
context.bindings
O objeto context.bindings
é usado para ler as entradas ou definir as saídas. O exemplo a seguir é um gatilho da fila de armazenamento, que usa context.bindings
para copiar uma entrada de blob de armazenamento para uma saída de blob de armazenamento. O conteúdo da mensagem da fila substitui {queueTrigger}
como o nome do arquivo a ser copiado, com a ajuda de uma expressão de associação.
{
"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
O método context.done
foi preterido. Antes das funções assíncronas serem suportadas, você sinalizaria que sua função foi concluída chamando context.done()
:
module.exports = function (context, request) {
context.log("this pattern is now deprecated");
context.done();
};
Agora, recomenda-se remover a chamada para context.done()
e marcar sua função como assíncrona para que ela retorne uma promessa (mesmo que você não faça nada await
). Assim que sua função for concluída (em outras palavras, a promessa retornada é resolvida), o modelo v3 sabe que sua função está concluída.
module.exports = async function (context, request) {
context.log("you don't need context.done or an awaited call")
};
Cada invocação de sua função é aprovada por um objeto de invocação context
, com informações sobre sua invocação e os métodos utilizados para o registro em log. No modelo v4, o objeto context
normalmente é o segundo argumento passado para seu manipulador.
A classe InvocationContext
tem as propriedades a seguir:
Propriedade | Descrição |
---|---|
invocationId |
A ID da invocação de função atual. |
functionName |
O nome da função. |
extraInputs |
Usada para obter os valores de entradas extras. Para obter mais informações, confira entradas e saídas extras. |
extraOutputs |
Usada para definir os valores de saídas extras. Para obter mais informações, confira entradas e saídas extras. |
retryContext |
Consulte o contexto da nova tentativa. |
traceContext |
O contexto para rastreamento distribuído. Para obter mais informações, consulte Trace Context . |
triggerMetadata |
Metadados sobre a entrada do gatilho para essa invocação, sem incluir o valor em si. Por exemplo, um gatilho do hub de eventos tem uma propriedade enqueuedTimeUtc . |
options |
As opções usadas ao registrar a função, depois de terem sido validadas e com padrões especificados explicitamente. |
Repetir o contexto
O objeto retryContext
tem as seguintes propriedades:
Propriedade | Descrição |
---|---|
retryCount |
Um número que representa a tentativa da nova tentativa atual. |
maxRetryCount |
O número máximo de vezes que uma execução foi repetida. Um valor de -1 significa tentar indefinidamente. |
exception |
Exceção que causou a nova tentativa. |
Para obter mais informações, consulte retry-policies
.
Registro em log
No Azure Functions, é recomendável usar context.log()
para gravar registros. O Azure Functions é integrado ao Azure Application Insights para capturar melhorar os logs do aplicativo de funções. O Application Insights, parte do Azure Monitor, fornece recursos para coleção, renderização visual e análise de logs de aplicativos e suas saídas de rastreamento. Para saber mais, consulte Monitorar o Azure Functions.
Observação
Se você usar o método alternativo console.log
Node.js, esses logs serão rastreados no nível do aplicativo e não serão associados a nenhuma função específica. É altamente recomendado usar context
para registro em log em vez de console
para que todos os logs sejam associados a uma função específica.
O exemplo a seguir grava um log no nível padrão de "informações", incluindo a ID de invocação:
context.log(`Something has happened. Invocation ID: "${context.invocationId}"`);
Níveis de log
Além do método padrão context.log
, estão disponíveis os seguintes métodos que permitem gravar logs em níveis específicos:
Método | Descrição |
---|---|
context.log.error() |
Grava um evento de nível de erro nos logs. |
context.log.warn() |
Grava um evento de nível de aviso nos logs. |
context.log.info() |
Grava um evento de nível de informações nos logs. |
context.log.verbose() |
Grava um evento de nível de rastreamento nos logs. |
Método | Descrição |
---|---|
context.trace() |
Grava um evento de nível de rastreamento nos logs. |
context.debug() |
Grava um evento de nível de depuração nos logs. |
context.info() |
Grava um evento de nível de informações nos logs. |
context.warn() |
Grava um evento de nível de aviso nos logs. |
context.error() |
Grava um evento de nível de erro nos logs. |
Configurar o nível do log
O Azure Functions permite que você defina o nível limite a ser usado ao rastrear e exibir os logs. Para definir o limite, use a propriedade logging.logLevel
no arquivo host.json
. Essa propriedade permite definir um nível padrão aplicado a todas as funções ou um limite para cada função individual. Para saber mais, confira Como configurar o monitoramento para o Azure Functions.
Acompanhamento de dados personalizados
Por padrão, o Azure Functions grava a saída como rastreamentos no Application Insights. Para mais controle, você pode usar o SDK Node.js do Application Insights para enviar dados personalizados para sua instância do 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});
};
O parâmetro tagOverrides
define operation_Id
para a ID de invocação de função. Esta configuração permite que você correlacione todos os logs gerados automaticamente e personalizados para uma determinada invocação de função.
Gatilhos HTTP
Os gatilhos HTTP e webhook usam objetos de solicitação e resposta para representar mensagens HTTP.
Os gatilhos HTTP e webhooks usam objetos HttpRequest
e HttpResponse
para representar mensagens HTTP. As classes representam um subconjunto do padrão fetch, usando o pacote undici
do Node.js.
Solicitação HTTP
A solicitação pode ser acessada de várias maneiras:
Como segundo argumento para sua função:
module.exports = async function (context, request) { context.log(`Http function processed request for url "${request.url}"`);
Da propriedade
context.req
:module.exports = async function (context, request) { context.log(`Http function processed request for url "${context.req.url}"`);
A partir das associações de entrada nomeadas: essa opção funciona da mesma forma que qualquer associação não HTTP. O nome de associação em
function.json
deve corresponder à chave emcontext.bindings
ou "request1" no exemplo a seguir:{ "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}"`);
O objeto HttpRequest
tem as seguintes propriedades:
Propriedade | Type | Descrição |
---|---|---|
method |
string |
Método de solicitação HTTP usado para invocar essa função. |
url |
string |
URL de solicitação. |
headers |
Record<string, string> |
Cabeçalhos de solicitação HTTP. Este objeto diferencia maiúsculas de minúsculas. É recomendável usar request.getHeader('header-name') em vez disso, o que não diferencia maiúsculas de minúsculas. |
query |
Record<string, string> |
Chaves e valores de parâmetros da cadeia de caracteres de consulta a partir da URL. |
params |
Record<string, string> |
Chaves e valores do parâmetros de rota. |
user |
HttpRequestUser | null |
Objeto que representa o usuário conectado, seja por meio da autenticação do Functions, da Autenticação SWA ou nulo quando esse usuário não está conectado. |
body |
Buffer | string | any |
Se o tipo de mídia for "application/octet-stream" ou "multipart/*", body será um Buffer. Se o valor for uma cadeia de caracteres analisável em JSON, body é o objeto analisado. Caso contrário, body será uma cadeia de caracteres. |
rawBody |
string |
O corpo como uma cadeia de caracteres. Apesar do nome, essa propriedade não retorna um Buffer. |
bufferBody |
Buffer |
O corpo como um buffer. |
A solicitação pode ser acessada como o primeiro argumento do seu manipulador para uma função disparada por HTTP.
async (request, context) => {
context.log(`Http function processed request for url "${request.url}"`);
O objeto HttpRequest
tem as seguintes propriedades:
Propriedade | Type | Descrição |
---|---|---|
method |
string |
Método de solicitação HTTP usado para invocar essa função. |
url |
string |
URL de solicitação. |
headers |
Headers |
Cabeçalhos de solicitação HTTP. |
query |
URLSearchParams |
Chaves e valores de parâmetros da cadeia de caracteres de consulta a partir da URL. |
params |
Record<string, string> |
Chaves e valores do parâmetros de rota. |
user |
HttpRequestUser | null |
Objeto que representa o usuário conectado, seja por meio da autenticação do Functions, da Autenticação SWA ou nulo quando esse usuário não está conectado. |
body |
ReadableStream | null |
Corpo como um fluxo legível. |
bodyUsed |
boolean |
Um valor booliano que indica se o corpo já foi lido. |
Para acessar o corpo de uma solicitação ou resposta, os seguintes métodos podem ser usados:
Método | Tipo de retorno |
---|---|
arrayBuffer() |
Promise<ArrayBuffer> |
blob() |
Promise<Blob> |
formData() |
Promise<FormData> |
json() |
Promise<unknown> |
text() |
Promise<string> |
Observação
As funções do corpo podem ser executadas apenas uma vez; chamadas subsequentes serão resolvidas com cadeia de caracteres vazias/ArrayBuffers.
Resposta HTTP
A resposta pode ser definida de várias maneiras:
Defina a propriedade
context.res
:module.exports = async function (context, request) { context.res = { body: `Hello, world!` };
Retorne a resposta: Se sua função for assíncrona e você definir o nome da associação como
$return
em seufunction.json
, você poderá retornar a resposta diretamente em vez de defini-la emcontext
.{ "type": "http", "direction": "out", "name": "$return" }
module.exports = async function (context, request) { return { body: `Hello, world!` };
Defina a associação de saída nomeada: essa opção funciona da mesma forma que qualquer associação não HTTP. O nome de associação em
function.json
deve corresponder à chave emcontext.bindings
ou "response1" no exemplo a seguir:{ "type": "http", "direction": "out", "name": "response1" }
module.exports = async function (context, request) { context.bindings.response1 = { body: `Hello, world!` };
Chamada
context.res.send()
: essa opção foi preterida. Ele chama implicitamentecontext.done()
e não pode ser usado em uma função assíncrona.module.exports = function (context, request) { context.res.send(`Hello, world!`);
Se você criar um novo objeto ao definir a resposta, esse objeto deverá corresponder à interface HttpResponseSimple
, que tem as seguintes propriedades:
Propriedade | Type | Descrição |
---|---|---|
headers |
Record<string, string> (opcional) |
Cabeçalhos de resposta HTTP. |
cookies |
Cookie[] (opcional) |
Cookies de resposta HTTP. |
body |
any (opcional) |
Corpo da resposta HTTP. |
statusCode |
number (opcional) |
Código de status de resposta HTTP. Se não estiver definida, o padrão será 200 . |
status |
number (opcional) |
O mesmo que statusCode . Esta propriedade será ignorada se statusCode estiver definida. |
Você também pode modificar o objeto context.res
sem sobrescrevê-lo. O objeto context.res
padrão usa a interface HttpResponseFull
, que suporta os seguintes métodos, além das propriedades HttpResponseSimple
:
Método | Descrição |
---|---|
status() |
Define o status. |
setHeader() |
Define um campo de cabeçalho. OBSERVAÇÃO: res.set() e res.header() também são suportados e fazem a mesma coisa. |
getHeader() |
Obter um campo de cabeçalho. OBSERVAÇÃO: res.get() também é suportado e faz a mesma coisa. |
removeHeader() |
Remover um cabeçalho. |
type() |
Define o cabeçalho "content-type". |
send() |
Esse método é preterido. Ele define o corpo e chama context.done() para indicar que uma função de sincronização foi concluída. OBSERVAÇÃO: res.end() também é suportado e faz a mesma coisa. |
sendStatus() |
Esse método é preterido. Isso define o código de status e chama context.done() para indicar que uma função de sincronização foi concluída. |
json() |
Esse método é preterido. Ele define o "content-type" como "application/json", define o corpo e chama context.done() para indicar que uma função de sincronização foi concluída. |
A resposta pode ser definida de várias maneiras:
Como uma interface simples com o tipo
HttpResponseInit
: essa opção é a maneira mais concisa de retornar respostas.return { body: `Hello, world!` };
A interface
HttpResponseInit
tem as propriedades a seguir:Propriedade Type Descrição body
BodyInit
(opcional)Corpo da resposta HTTP como um dos ArrayBuffer
,AsyncIterable<Uint8Array>
,Blob
,FormData
,Iterable<Uint8Array>
,NodeJS.ArrayBufferView
,URLSearchParams
,null
oustring
.jsonBody
any
(opcional)Um corpo de resposta HTTP serializado em JSON. Se definida, a propriedade HttpResponseInit.body
será ignorada em favor dessa propriedade.status
number
(opcional)Código de status de resposta HTTP. Se não estiver definida, o padrão será 200
.headers
HeadersInit
(opcional)Cabeçalhos de resposta HTTP. cookies
Cookie[]
(opcional)Cookies de resposta HTTP. Como uma classe com o tipo
HttpResponse
: essa opção fornece métodos auxiliares para ler e modificar várias partes da resposta, como os cabeçalhos.const response = new HttpResponse({ body: `Hello, world!` }); response.headers.set('content-type', 'application/json'); return response;
A classe
HttpResponse
aceita umHttpResponseInit
opcional como um argumento para seu construtor e tem as seguintes propriedades:Propriedade Type Descrição status
number
Código de status de resposta HTTP. headers
Headers
Cabeçalhos de resposta HTTP. cookies
Cookie[]
Cookies de resposta HTTP. body
ReadableStream | null
Corpo como um fluxo legível. bodyUsed
boolean
Um booleano indicando se o corpo já foi lido.
Fluxos HTTP
Os fluxos HTTP são um recurso que torna mais fácil processar dados grandes, transmitir respostas do OpenAI, fornecer conteúdo dinâmico e dar suporte a outros cenários HTTP básicos. Permite transmitir solicitações e respostas de pontos de extremidade HTTP no seu aplicativo de funções Node.js. Use fluxos HTTP nos cenários em que seu aplicativo requer intercâmbio e interação entre cliente e servidor por HTTP em tempo real. Você também pode usar fluxos HTTP para obter o melhor desempenho e confiabilidade para seus aplicativos quando usar HTTP.
Importante
Não há suporte para fluxos HTTP no modelo v3. Atualize para o modelo v4 para usar o recurso de fluxos HTTP.
Os tipos HttpRequest
e HttpResponse
existentes no modelo de programação v4 já dão suporte a várias maneiras de lidar com o corpo da mensagem, incluindo como sendo um fluxo.
Pré-requisitos
- O pacote npm do
@azure/functions
versão 4.3.0 ou posterior. - O runtime do Azure Functions versão 4.28 ou posterior.
- O Azure Functions Core Tools versão 4.0.5530 ou posterior, que contém a versão correta do runtime.
Habilitar fluxos
Use essas etapas para habilitar fluxos HTTP no seu aplicativo de funções no Azure e em seus projetos locais:
Se estiver planejando transmitir grandes quantidades de dados, modifique a configuração
FUNCTIONS_REQUEST_BODY_SIZE_LIMIT
no Azure. O tamanho máximo de corpo padrão permitido é104857600
, o que limita suas solicitações a um tamanho de aproximadamente 100 MB.Para o desenvolvimento local, adicione também
FUNCTIONS_REQUEST_BODY_SIZE_LIMIT
ao arquivo local.settings.json.Adicione o código a seguir ao seu aplicativo em qualquer arquivo incluído por seu campo principal.
const { app } = require('@azure/functions'); app.setup({ enableHttpStream: true });
Exemplos de fluxos
Esse exemplo mostra uma função disparada por HTTP que recebe dados por meio de uma solicitação HTTP POST; a função transmite esses dados para um arquivo de saída 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!' };
},
});
Esse exemplo mostra uma função disparada por HTTP que transmite o conteúdo de um arquivo como resposta às solicitações HTTP GET recebidas:
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 um aplicativo de exemplo pronto para execução usando fluxos, confira este exemplo no GitHub.
Considerações sobre fluxo
- Use
request.body
para se beneficiar ao máximo do uso de fluxos. Você ainda pode continuar usando métodos comorequest.text()
, que sempre retornam o corpo como uma cadeia de caracteres.
Ganchos
Não há suporte para ganchos no modelo v3. Atualize para o modelo v4 para usar os ganchos.
Use um gancho para executar código em pontos diferentes no ciclo de vida do Azure Functions. Os ganchos são executados na ordem em que estão registrados e podem ser registrados em qualquer arquivo em seu aplicativo. Atualmente, há dois escopos de ganchos, nível de "aplicativo" e nível de "invocação".
Ganchos de invocação
Os ganchos de invocação são executados uma vez por invocação da função, antes em um gancho preInvocation
ou depois em um gancho postInvocation
. Por padrão, o gancho é executado para todos os tipos de gatilho, mas você também pode filtrar por tipo. O exemplo a seguir mostra como registrar um gancho de invocação e filtrar por tipo de gatilho:
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}`
);
}
});
O primeiro argumento para o manipulador de gancho é um objeto de contexto específico para esse tipo de gancho.
O objeto PreInvocationContext
tem as seguintes propriedades:
Propriedade | Descrição |
---|---|
inputs |
Os argumentos passados para a invocação. |
functionHandler |
O manipulador de funções para a invocação. As alterações nesse valor afetam a função em si. |
invocationContext |
O objeto de contexto de invocação passado para a função. |
hookData |
O local recomendado para armazenar e compartilhar dados entre ganchos no mesmo escopo. Use um nome de propriedade exclusivo para que ele não entre em conflito com os dados de outros ganchos. |
O objeto PostInvocationContext
tem as seguintes propriedades:
Propriedade | Descrição |
---|---|
inputs |
Os argumentos passados para a invocação. |
result |
O resultado da função. As alterações nesse valor afetam o resultado geral da função. |
error |
O erro gerado pela função ou nulo/indefinido se não houver erro. As alterações nesse valor afetam o resultado geral da função. |
invocationContext |
O objeto de contexto de invocação passado para a função. |
hookData |
O local recomendado para armazenar e compartilhar dados entre ganchos no mesmo escopo. Use um nome de propriedade exclusivo para que ele não entre em conflito com os dados de outros ganchos. |
Ganchos de aplicativo
Os ganchos de aplicativo são executados uma vez por instância do aplicativo, durante a inicialização em um gancho appStart
ou durante o término em um gancho appTerminate
. Os ganchos de término de aplicativo têm um tempo limitado para serem executados e não são executados em todos os cenários.
O runtime do Azure Functions atualmente não dá suporte ao registro em log de contexto fora de uma invocação. Use o pacote npm do Application Insights para registrar dados durante os ganchos no nível do aplicativo.
O exemplo a seguir registra ganchos de aplicativo:
const { app } = require('@azure/functions');
app.hook.appStart((context) => {
// add your logic here
});
app.hook.appTerminate((context) => {
// add your logic here
});
O primeiro argumento para o manipulador de gancho é um objeto de contexto específico para esse tipo de gancho.
O objeto AppStartContext
tem as seguintes propriedades:
Propriedade | Descrição |
---|---|
hookData |
O local recomendado para armazenar e compartilhar dados entre ganchos no mesmo escopo. Use um nome de propriedade exclusivo para que ele não entre em conflito com os dados de outros ganchos. |
O objeto AppTerminateContext
tem as seguintes propriedades:
Propriedade | Descrição |
---|---|
hookData |
O local recomendado para armazenar e compartilhar dados entre ganchos no mesmo escopo. Use um nome de propriedade exclusivo para que ele não entre em conflito com os dados de outros ganchos. |
Dimensionamento e simultaneidade
Por padrão, o Azure Functions monitora automaticamente a carga em seu aplicativo e cria mais instâncias de host para Node.js conforme a necessidade. O Azure Functions usa limites internos (não configuráveis pelo usuário) em tipos de gatilhos diferentes para decidir quando adicionar instâncias, por exemplo, a idade das mensagens e o tamanho da fila para QueueTrigger. Para obter mais informações, confira Como funcionam os planos de consumo e Premium.
Esse comportamento de escala é suficiente para vários aplicativos Node.js. Para aplicativos associados à CPU, você pode aprimorar ainda mais o desempenho usando vários processos de trabalho de linguagem. Você pode aumentar o número de processos de trabalho por host do padrão de 1 até um máximo de 10 usando a configuração do aplicativo FUNCTIONS_WORKER_PROCESS_COUNT. O Azure Functions tenta distribuir uniformemente invocações de função simultâneas entre esses trabalhos. Este comportamento faz com que uma função com uso intensivo de CPU seja menos propensa a bloquear a execução de outras funções. A configuração se aplica a cada host que o Azure Functions cria ao escalar seu aplicativo para atender à demanda.
Aviso
Use a configuração FUNCTIONS_WORKER_PROCESS_COUNT
com cautela. Vários processos em execução na mesma instância podem levar a um comportamento imprevisível e aumentar os tempos de carregamento da função. Se você usar essa configuração, é altamente recomendável compensar essas desvantagens executando a partir de um arquivo de pacote.
Versão do nó
Você pode ver a versão atual usada pelo runtime registrando em log o process.version
de qualquer função. Veja supported versions
para obter uma lista de versões do Node.js compatíveis com cada modelo de programação.
Definir a versão do Node
A maneira como você atualiza sua versão do Node.js depende do sistema operacional no qual seu aplicativo de funções é executado.
Ao executar no Windows, a versão do Node.js é definida pela configuração WEBSITE_NODE_DEFAULT_VERSION
do aplicativo. Essa configuração pode ser atualizada usando a CLI do Azure ou no portal do Azure.
Para obter mais informações sobre as versões disponíveis, consulte Versões com suporte.
Antes de atualizar sua versão do Node.js, verifique se o aplicativo de funções está em execução na versão mais recente do runtime do Azure Functions. Se você precisar atualizar sua versão de runtime, consulte Migrar aplicativos do Azure Functions versão 3.x para a versão 4.x.
Execute o comando az functionapp config appsettings set
da CLI do Azure para atualizar a versão do Node.js para seu aplicativo de funções em execução no Windows:
az functionapp config appsettings set --settings WEBSITE_NODE_DEFAULT_VERSION=~20 \
--name <FUNCTION_APP_NAME> --resource-group <RESOURCE_GROUP_NAME>
Isso define a WEBSITE_NODE_DEFAULT_VERSION
configuração de aplicativo para a versão LTS com suporte do ~20
.
Depois que as alterações são feitas, o aplicativo de funções é reiniciado. Para saber mais sobre o suporte do runtime do Azure Functions, consulte a Política de suporte ao runtime de linguagem.
Variáveis de ambiente
As variáveis de ambiente podem ser úteis para segredos operacionais (cadeias de conexão, chaves, pontos de extremidade, etc.) ou configurações ambientais, como variáveis de perfil. Você pode adicionar variáveis de ambiente em seus ambientes local e em nuvem e acessá-las por meio de process.env
no seu código de função.
O exemplo a seguir registra a variável de ambiente 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"]}`);
}
No ambiente de desenvolvimento local
Quando você executa localmente, seu projeto de funções inclui um local.settings.json
arquivo, onde você armazena suas variáveis de ambiente no objeto Values
.
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "",
"FUNCTIONS_WORKER_RUNTIME": "node",
"CUSTOM_ENV_VAR_1": "hello",
"CUSTOM_ENV_VAR_2": "world"
}
}
No ambiente de nuvem do Azure
Quando você executa no Azure, o aplicativo de funções permite que você defina e use as Configurações do Aplicativo, como cadeias de conexão de serviço, e expõe essas configurações como variáveis de ambiente durante a execução.
Há várias maneiras de adicionar, atualizar e excluir configurações do aplicativo de funções:
As alterações nas configurações do aplicativo de funções exigem que o seu aplicativo de funções seja reiniciado.
Variáveis de ambiente de trabalho
Existem várias variáveis de ambiente do Functions específicas para Node.js:
languageWorkers__node__arguments
Essa configuração permite que você especifique argumentos personalizados ao iniciar seu processo do Node.js. Geralmente é usado localmente para iniciar o trabalho no modo de depuração, mas também pode ser usado no Azure se você precisar de argumentos personalizados.
Aviso
Se possível, evite usar languageWorkers__node__arguments
no Azure, pois isso pode ter um efeito negativo nos horários de inicialização a frio. Em vez de usar trabalhos pré-aquecidos, o runtime deve iniciar um novo trabalhador do zero com seus argumentos personalizados.
logging__logLevel__Worker
Esta configuração ajusta o nível do log padrão para os logs de trabalho específicos do Node.js. Por padrão, apenas os logs de aviso ou erro são mostrados, mas você pode defini-lo como information
ou debug
para ajudar a diagnosticar problemas com o trabalhador Node.js. Para obter mais informações, consulte configurando níveis de log.
Módulos ECMAScript (versão prévia)
Observação
Como os módulos ECMAScript são uma versão prévia do recurso atualmente no Node.js 14 ou superior no Azure Functions.
Os módulos ECMAScript (módulos ES) são o novo sistema oficial de módulo padrão para Node.js. Até agora, os exemplos de código neste artigo usam a sintaxe CommonJS. Ao executar o Azure Functions no Node.js 14 ou posterior, você pode optar por gravar suas funções usando a sintaxe dos módulos ES.
Para usar os módulos ES em uma função, altere seu nome de arquivo para usar uma extensão .mjs
. O exemplo de arquivo index.mjs a seguir é uma função disparada por HTTP que usa a sintaxe de módulos ES para importar a biblioteca uuid
e retornar um 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
});
Configurar o ponto de entrada de função
As propriedades scriptFile
e entryPoint
do function.json
podem ser usadas para configurar o local e o nome da função exportada. A propriedade scriptFile
é necessária quando você está usando o TypeScript e deve apontar para o JavaScript compilado.
Usando scriptFile
Por padrão, uma função JavaScript é executada do index.js
, um arquivo que compartilha o mesmo diretório pai que seu function.json
correspondente.
scriptFile
pode ser usado para obter uma estrutura de pastas semelhante ao exemplo a seguir:
<project_root>/
| - node_modules/
| - myFirstFunction/
| | - function.json
| - lib/
| | - sayHello.js
| - host.json
| - package.json
O function.json
da myFirstFunction
deve incluir uma propriedade scriptFile
que aponte para o arquivo com a função exportada a ser executada.
{
"scriptFile": "../lib/sayHello.js",
"bindings": [
...
]
}
Usando entryPoint
No modelo v3, uma função deve ser exportada usando module.exports
para ser encontrada e executada. Por padrão, a função que é executada quando disparada é a única exportação desse arquivo, a exportação denominada run
ou a exportação denominada index
. O exemplo a seguir define entryPoint
em function.json
como um valor personalizado, "logHello":
{
"entryPoint": "logHello",
"bindings": [
...
]
}
async function logHello(context) {
context.log('Hello, world!');
}
module.exports = { logHello };
Depuração local
Recomenda-se usar o VS Code para depuração local, que inicia seu processo Node.js no modo de depuração automaticamente e se anexa ao processo para você. Para obter mais informações, confira executar a função localmente.
Se você estiver usando uma ferramenta diferente para depuração ou quiser iniciar o processo Node.js no modo de depuração manualmente, adicione "languageWorkers__node__arguments": "--inspect"
em Values
no seu local.settings.json. O argumento --inspect
diz ao Node.js para ouvir um cliente de depuração, na porta 9229 por padrão. Para obter mais informações, consulte o guia de depuração do Node.js.
Recomendações
Essa seção descreve vários padrões impactantes para aplicativos Node.js que recomendamos que você siga.
Escolher Planos do Serviço de Aplicativo de vCPU único
Ao criar um aplicativo de funções que usa o Plano do Serviço de Aplicativo, recomendamos que você selecione um plano de vCPU único em vez de um plano com vários vCPUs. Atualmente, o Functions executa funções em Node.js com mais eficiência em VMs de vCPU único, e o uso de VMs maiores não produz os aprimoramentos de desempenho esperados. Quando for necessário, você poderá escalar horizontalmente, de modo manual, adicionando mais instâncias de VM de vCPU único ou poderá habilitar o dimensionamento automático. Para saber mais, confira Dimensionar a contagem de instâncias manual ou automaticamente.
Executar a partir de um arquivo do pacote
Quando você desenvolve Azure Functions no modelo de hospedagem sem servidor, as inicializações a frio são uma realidade. Inicialização a frio refere-se à primeira vez que seu aplicativo de função é iniciado após um período de inatividade, demorando mais para ser iniciado. Para aplicativos Node.js com grandes árvores de dependência em particular, a inicialização a frio pode ser significativa. Para acelerar o processo de inicialização a frio, execute suas funções como um arquivo de pacote quando possível. Muitos métodos de implantação usam este modelo por padrão, mas se você estiver passando por grandes inicializações a frio, você deve verificar para ter certeza de está executando dessa maneira.
Usar um único cliente estático
Ao usar um cliente específico de serviço em um aplicativo do Azure Functions, não crie um novo cliente a cada invocação de função, pois você pode atingir os limites de conexão. Em vez disso, crie um único cliente estático no escopo global. Para saber mais, confira Gerenciar conexões no Azure Functions.
Usar async
e await
Ao escrever o Azure Functions em Node.js, você deve escrever o código usando as palavras-chave async
e await
. Escrever código usando async
e await
em vez de retornos de chamada ou .then
e .catch
com os Promises ajuda a evitar dois problemas comuns:
- Lançamento de exceções não capturadas que falham no processo do Node.js, potencialmente afetando a execução de outras funções.
- Comportamento inesperado, como logs ausentes de
context.log
, causado por chamadas assíncronas que não são devidamente aguardadas.
No seguinte exemplo, o método assíncrono fs.readFile
é invocado com uma função de retorno de chamada que mostra erros em primeiro lugar como seu segundo parâmetro. Este código causa ambos os problemas mencionados anteriormente. Uma exceção que não seja explicitamente capturada no escopo correto pode travar todo o processo (edição #1). Retornar sem garantir que o retorno de chamada seja concluído significa que, às vezes, a resposta http terá um corpo vazio (problema 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 };
},
});
No seguinte exemplo, o método assíncrono fs.readFile
é invocado com uma função de retorno de chamada que mostra erros em primeiro lugar como seu segundo parâmetro. Este código causa ambos os problemas mencionados anteriormente. Uma exceção que não seja explicitamente capturada no escopo correto pode travar todo o processo (edição #1). Chamar o método preterido context.done()
fora do escopo do retorno de chamada pode sinalizar que a função foi concluída antes que o arquivo fosse lido (problema #2). Neste exemplo, chamar context.done()
muito cedo resulta em entradas de log ausentes começando com 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 as palavras-chave async
e await
para ajudar a evitar esses dois problemas. A maioria das APIs no ecossistema Node.js foram convertidas para suportar promessas de alguma forma. Por exemplo, a partir da v14, o Node.js fornece uma API fs/promises
para substituir a API de retorno de chamada fs
.
No exemplo a seguir, todas as exceções não tratadas lançadas durante a execução da função só falham na invocação individual que gerou a exceção. A palavra-chave await
significa que as etapas seguintes a readFile
só serão executadas após serem concluídas.
// 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;
}
},
});
Com async
e await
, você também não precisa chamar o retorno de chamada de 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}`);
}
Solucionar problemas
Confira o Guia para solucionar problemas de Node.js.
Próximas etapas
Para saber mais, consulte os recursos a seguir: