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 propriedade name definida em function.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 à propriedade name definida em function.json.

    module.exports = async function (context) {
        context.log("This is myTrigger: " + context.bindings.myTrigger);
        context.log("This is myInput: " + context.bindings.myInput);
        context.log("This is myOtherInput: " + context.bindings.myOtherInput);
    };
    

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 em function.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 em context.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 e binary são suportados (stream não é)
  • Para entradas HTTP, a propriedade dataType é ignorada. Em vez disso, use propriedades no objeto request 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 em context.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 seu function.json, você poderá retornar a resposta diretamente em vez de defini-la em context.

    {
      "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 em context.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 implicitamente context.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 ou string.
    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 um HttpResponseInit 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 (versão prévia)

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.

Os fluxos HTTP estão atualmente em versão prévia.

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

Habilitar fluxos

Use essas etapas para habilitar fluxos HTTP no seu aplicativo de funções no Azure e em seus projetos locais:

  1. 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.

  2. Para o desenvolvimento local, adicione também FUNCTIONS_REQUEST_BODY_SIZE_LIMIT ao arquivo local.settings.json.

  3. 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 };
    },
});

Considerações sobre fluxo

  • Use request.body para se beneficiar ao máximo do uso de fluxos. Você ainda pode continuar usando métodos como request.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: