Partilhar via


Guia do desenvolvedor do Azure Functions Node.js

Este guia é uma introdução ao desenvolvimento do Azure Functions usando JavaScript ou TypeScript. O artigo pressupõe que você já tenha lido o guia do desenvolvedor do Azure Functions.

Importante

O conteúdo deste artigo muda com base na 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 @azure/functions pacote npm que você está usando em seu aplicativo. Se você não tiver esse pacote listado no , package.jsono padrão será v3. Saiba mais sobre as diferenças entre v3 e v4 no guia de migração.

Como desenvolvedor 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 tempo de execução do Azure Functions:
    • Modelo de programação: define como você cria seu código e é específico para JavaScript e TypeScript.
    • Tempo de execução: define o comportamento subjacente do Azure Functions e é compartilhado em todos os idiomas.
  • A versão do modelo de programação está estritamente ligada à versão do @azure/functions pacote npm. É versionado independentemente do tempo de execução. Tanto o tempo de execução quanto o modelo de programação usam o número 4 como sua última versão principal, mas isso é uma coincidência.
  • Não é possível misturar os modelos de programação v3 e v4 no mesmo aplicativo de função. Assim que você registrar uma função v4 em seu aplicativo, todas as funções v3 registradas em arquivos function.json serão ignoradas.

Versões suportadas

A tabela a seguir mostra cada versão do modelo de programação Node.js juntamente com suas versões com suporte do tempo de execução e Node.js do Azure Functions.

Versão do modelo de programação Nível de Suporte Versão do Functions Runtime Versão Node.js Description
4.x GA 4.25+ 20,x, 18,x Suporta 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 ligaçõ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. Consulte Versões de funções 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. Consulte Versões de funções para obter mais informações.

Estrutura de pastas

A estrutura de pastas necessária para um projeto JavaScript se parece com o exemplo a seguir:

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

A pasta principal do projeto, <project_root>, pode conter os seguintes arquivos:

  • .vscode/: (Opcional) Contém a configuração armazenada do Visual Studio Code. Para saber mais, consulte Configurações de código do Visual Studio.
  • myFirstFunction/function.json: Contém a configuração para o gatilho, entradas e 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, consulte usando scriptFile.
  • .funcignore: (Opcional) Declara arquivos que não devem ser publicados no Azure. Normalmente, esse arquivo contém .vscode/ para ignorar a configuração do editor, testar/ ignorar casos de teste e local.settings.json para impedir que as configurações do aplicativo local sejam publicadas.
  • host.json: Contém opções de configuração que afetam todas as funções em uma instância de aplicativo de função. Esse arquivo é publicado no Azure. Nem todas as opções são suportadas quando executadas localmente. Para saber mais, consulte host.json.
  • local.settings.json: Usado para armazenar configurações de aplicativos e cadeias de conexão quando ele está sendo executado localmente. Este ficheiro não é publicado no Azure. Para saber mais, consulte 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 se parece com o 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 pasta principal do projeto, <project_root>, pode conter os seguintes arquivos:

  • .vscode/: (Opcional) Contém a configuração armazenada do Visual Studio Code. Para saber mais, consulte Configurações de código do Visual Studio.
  • src/functions/: O local padrão para todas as funções e seus gatilhos e ligações relacionados.
  • test/: (Opcional) Contém os casos de teste do seu aplicativo de função.
  • .funcignore: (Opcional) Declara arquivos que não devem ser publicados no Azure. Normalmente, esse arquivo contém .vscode/ para ignorar a configuração do editor, testar/ ignorar casos de teste e local.settings.json para impedir que as configurações do aplicativo local sejam publicadas.
  • host.json: Contém opções de configuração que afetam todas as funções em uma instância de aplicativo de função. Esse arquivo é publicado no Azure. Nem todas as opções são suportadas quando executadas localmente. Para saber mais, consulte host.json.
  • local.settings.json: Usado para armazenar configurações de aplicativos e cadeias de conexão quando ele está sendo executado localmente. Este ficheiro não é publicado no Azure. Para saber mais, consulte 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.

Registar uma função

O modelo v3 registra uma função baseada na existência de dois arquivos. Primeiro, você precisa de um function.json arquivo 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 index.js arquivo na mesma pasta que o .function.json Se você estiver usando TypeScript, deverá usar a scriptFile propriedade in function.json para apontar para o arquivo JavaScript compilado. Para personalizar o local 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 exportada deve ser sempre declarada como uma async function no modelo v3. Você pode exportar uma função síncrona, mas então você deve ligar context.done() para sinalizar que sua função está concluída, o que é 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 acionada 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 main campo em seu package.json. Você pode definir o main campo como um único arquivo ou vários arquivos usando um padrão glob. A tabela a seguir mostra valores de exemplo para o main campo:

Exemplo Description
src/index.js Registre funções a partir de um único arquivo raiz.
src/functions/*.js Registe cada função a partir do seu próprio ficheiro.
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 app objeto do @azure/functions módulo npm e chamar o método específico para seu tipo de gatilho. O primeiro argumento ao registrar uma função é o nome da função. O segundo argumento é um options objeto que especifica a configuração para seu gatilho, seu manipulador e quaisquer outras entradas ou saídas. Em alguns casos em que a configuração do gatilho não é necessária, você pode passar o manipulador diretamente como o segundo argumento em vez de um options objeto.

O registo de uma função pode ser feito a partir de qualquer ficheiro no seu projeto, desde que esse ficheiro seja carregado (direta ou indiretamente) com base no main campo no seu package.json ficheiro. A função deve ser registrada em um escopo global porque você não pode registrar funções depois que as execuções forem iniciadas.

O exemplo a seguir é uma função simples que registra que ela foi acionada 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 é necessária para ter exatamente uma entrada primária chamada gatilho. Pode também ter entradas e/ou saídas secundárias. As entradas e saídas são configuradas em seus function.json arquivos e também são chamadas de associações.

Entradas

As entradas são ligações com direction definido como in. A principal diferença entre um gatilho e uma entrada secundária é que o para um gatilho type termina em Trigger, por exemplo, tipo blobTrigger vs tipo blob. A maioria das funções usa apenas um gatilho e não há suporte para muitos tipos de entrada secundária.

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 name propriedade 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 à name propriedade 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 ligações com direction set to 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 name propriedade da ligação de saída para $return in function.json like 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] Retornar um objeto contendo todas as saídas: se você estiver usando uma função assíncrona, poderá retornar um objeto com uma propriedade correspondente ao nome de cada associação no .function.json O exemplo a seguir usa ligações de saída chamadas "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
        };
    };
    
  • Definir 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 valores diretamente no context.bindings, onde a chave corresponde ao nome da ligação. O exemplo a seguir usa ligações de saída chamadas "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 ligações

Você pode usar a dataType propriedade em uma associação de entrada para alterar o tipo de 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 dataType propriedade é ignorada. Em vez disso, use propriedades no request objeto 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 como binary, o tipo mudará 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 é necessária para ter exatamente uma entrada primária chamada gatilho. Pode também 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 referidas como ligações fora do contexto do modelo de programação Node.js. Antes da v4 do modelo, essas ligações eram configuradas em function.json arquivos.

Entrada de gatilho

O gatilho é a única entrada ou saída necessária. Para a app maioria dos tipos de gatilho, você registra uma função usando um método no objeto nomeado após o tipo de gatilho. Você pode especificar a configuração específica para o gatilho diretamente no options argumento. Por exemplo, um gatilho HTTP permite especificar 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, você especifica a options configuração de retorno no argumento com a ajuda do objeto exportado output do @azure/functions módulo. 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 options argumento ao registrar uma função. Os input objetos e output exportados do @azure/functions módulo fornecem métodos específicos do tipo 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 que é copiada para uma saída de blob de armazenamento extra. A mensagem de fila deve ser o nome de um arquivo e substitui {queueTrigger} como o nome de blob a ser copiado, com a ajuda de uma expressão de ligaçã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

O app, trigger, inpute output os objetos exportados pelo módulo fornecem métodos específicos de tipo para a @azure/functions maioria dos tipos. Para todos os tipos que não são suportados, um generic método é fornecido para permitir que você especifique manualmente a configuração. O generic método também pode ser usado se você quiser alterar as configurações padrão fornecidas por um método específico do tipo.

O exemplo a seguir é uma função acionada HTTP simples usando métodos genéricos em vez de métodos específicos do 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 o manipulador.

O context objeto tem as seguintes propriedades:

Property Description
invocationId A ID da invocação da função atual.
executionContext Consulte o contexto de execução.
bindings Consulte as ligações.
bindingData Metadados sobre a entrada de gatilho para essa invocação, não incluindo o valor em si. Por exemplo, um gatilho de hub de eventos tem uma enqueuedTimeUtc propriedade.
traceContext O contexto para o rastreamento distribuído. Para obter mais informações, veja Trace Context.
bindingDefinitions A configuração de suas entradas e saídas, conforme definido em function.json.
req Consulte Solicitação HTTP.
res Consulte Resposta HTTP.

context.executionContext

O context.executionContext objeto tem as seguintes propriedades:

Property Description
invocationId A ID da invocação da função atual.
functionName O nome da função que está sendo invocada. O nome da pasta que contém o function.json arquivo determina o nome da função.
functionDirectory A pasta que contém o function.json arquivo.
retryContext Consulte o contexto de repetição.

context.executionContext.retryContext

O context.executionContext.retryContext objeto tem as seguintes propriedades:

Property Description
retryCount Um número que representa a tentativa de repetição atual.
maxRetryCount Número máximo de vezes que uma execução é repetida. Um valor de -1 meios para tentar novamente indefinidamente.
exception Exceção que causou a nova tentativa.

context.bindings

O context.bindings objeto é usado para ler entradas ou definir saídas. O exemplo a seguir é um gatilho de 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 de fila substitui {queueTrigger} como o nome do arquivo a ser copiado, com a ajuda de uma expressão de ligaçã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;
};

contexto.done

O context.done método foi preterido. Antes de as funções assíncronas serem suportadas, você sinalizaria que sua função é feita chamando context.done():

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

Agora, é recomendável remover a chamada e context.done() marcar sua função como assíncrona para que ela retorne uma promessa (mesmo que você não await faça nada). Assim que sua função termina (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 é passada um objeto de invocação context , com informações sobre sua invocação e métodos usados para registro. No modelo v4, o context objeto é normalmente o segundo argumento passado para o manipulador.

A InvocationContext classe tem as seguintes propriedades:

Property Description
invocationId A ID da invocação da função atual.
functionName O nome da função.
extraInputs Usado para obter os valores de entradas extras. Para obter mais informações, consulte entradas e saídas extras.
extraOutputs Usado para definir os valores de saídas extras. Para obter mais informações, consulte entradas e saídas extras.
retryContext Consulte o contexto de repetição.
traceContext O contexto para o rastreamento distribuído. Para obter mais informações, veja Trace Context.
triggerMetadata Metadados sobre a entrada de gatilho para essa invocação, não incluindo o valor em si. Por exemplo, um gatilho de hub de eventos tem uma enqueuedTimeUtc propriedade.
options As opções usadas ao registrar a função, depois de validadas e com os padrões explicitamente especificados.

Contexto de repetição

O retryContext objeto tem as seguintes propriedades:

Property Description
retryCount Um número que representa a tentativa de repetição atual.
maxRetryCount Número máximo de vezes que uma execução é repetida. Um valor de -1 meios para tentar novamente indefinidamente.
exception Exceção que causou a nova tentativa.

Para obter mais informações, veja retry-policies.

Registo

No Azure Functions, é recomendável usar context.log() para gravar logs. O Azure Functions integra-se com o Azure Application Insights para capturar melhor os logs do aplicativo de função. O Application Insights, parte do Azure Monitor, fornece recursos para coleta, renderização visual e análise de logs de aplicativos e suas saídas de rastreamento. Para saber mais, consulte Monitoramento do Azure Functions.

Nota

Se você usar o método Node.js console.log alternativo, esses logs serão rastreados no nível do aplicativo e não serão associados a nenhuma função específica. É altamente recomendável 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 "information" padrão, incluindo a ID de invocação:

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

Níveis de registo

Além do método padrão context.log , os seguintes métodos estão disponíveis que permitem gravar logs em níveis específicos:

Método Description
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ção nos logs.
context.log.verbose() Grava um evento de nível de rastreamento nos logs.
Método Description
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ção 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 nível de log

O Azure Functions permite definir o nível de limite a ser usado ao rastrear e exibir logs. Para definir o limite, use a logging.logLevel host.json propriedade no arquivo. 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, consulte Como configurar o monitoramento para o Azure Functions.

Rastrear dados personalizados

Por padrão, o Azure Functions grava a saída como rastreamentos no Application Insights. Para obter mais controle, você pode usar o SDK do Application Insights Node.js 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 tagOverrides parâmetro define o ID de invocação da operation_Id função. Essa configuração permite correlacionar todos os logs personalizados e gerados automaticamente para uma determinada chamada de função.

Gatilhos HTTP

Os gatilhos HTTP e webhook usam objetos de solicitação e resposta para representar mensagens HTTP.

HTTP e webhook aciona o uso HttpRequest e HttpResponse objetos para representar mensagens HTTP. As classes representam um subconjunto do padrão de busca, usando o pacote do undici Node.js.

Solicitação HTTP

O pedido pode ser acedido de várias formas:

  • Como segundo argumento para a sua função:

    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${request.url}"`);
    
  • Da context.req propriedade:

    module.exports = async function (context, request) {
        context.log(`Http function processed request for url "${context.req.url}"`);
    
  • A partir das ligações de entrada nomeadas: Esta opção funciona da mesma forma que qualquer ligação não HTTP. O nome da 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 HttpRequest objeto tem as seguintes propriedades:

Propriedade Type Description
method string Método de solicitação HTTP usado para invocar essa função.
url string URL da solicitação.
headers Record<string, string> Cabeçalhos de solicitação HTTP. Este objeto diferencia maiúsculas de minúsculas. Recomenda-se usar request.getHeader('header-name') em vez disso, o que não diferencia maiúsculas de minúsculas.
query Record<string, string> Consultar chaves e valores de parâmetros de cadeia de caracteres da URL.
params Record<string, string> Chaves e valores de parâmetros de rota.
user HttpRequestUser | null Objeto que representa o usuário conectado, seja por meio da autenticação de funções, autenticação SWA ou nulo quando nenhum usuário estiver conectado.
body Buffer | string | any Se o tipo de mídia for "application/octet-stream" ou "multipart/*", body é um Buffer. Se o valor for uma cadeia de caracteres passível de análise JSON, body será o objeto analisado. Caso contrário, body é uma cadeia de caracteres.
rawBody string O corpo como uma corda. Apesar do nome, essa propriedade não retorna um Buffer.
bufferBody Buffer O corpo como um tampão.

A solicitação pode ser acessada como o primeiro argumento para seu manipulador para uma função acionada por HTTP.

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

O HttpRequest objeto tem as seguintes propriedades:

Propriedade Type Description
method string Método de solicitação HTTP usado para invocar essa função.
url string URL da solicitação.
headers Headers Cabeçalhos de solicitação HTTP.
query URLSearchParams Consultar chaves e valores de parâmetros de cadeia de caracteres da URL.
params Record<string, string> Chaves e valores de parâmetros de rota.
user HttpRequestUser | null Objeto que representa o usuário conectado, seja por meio da autenticação de funções, autenticação SWA ou nulo quando nenhum usuário estiver conectado.
body ReadableStream | null Corpo como um fluxo legível.
bodyUsed boolean Um booleano indicando se o corpo já está lido.

Para aceder ao corpo de um pedido ou resposta, podem ser utilizados os seguintes métodos:

Método Tipo de Retorno
arrayBuffer() Promise<ArrayBuffer>
blob() Promise<Blob>
formData() Promise<FormData>
json() Promise<unknown>
text() Promise<string>

Nota

As funções do corpo podem ser executadas apenas uma vez; as chamadas subsequentes serão resolvidas com strings/ArrayBuffers vazias.

Resposta HTTP

A resposta pode ser definida de várias maneiras:

  • Defina a context.res propriedade:

    module.exports = async function (context, request) {
        context.res = { body: `Hello, world!` };
    
  • Retornar a resposta: Se a função estiver assíncrona e você definir o nome da vinculação como $return no function.json, poderá retornar a resposta diretamente em vez de defini-la no context.

    {
      "type": "http",
      "direction": "out",
      "name": "$return"
    }
    
    module.exports = async function (context, request) {
        return { body: `Hello, world!` };
    
  • Definir a ligação de saída nomeada: esta opção funciona da mesma forma que qualquer ligação não HTTP. O nome da 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(): Esta opção foi preterida. Ele chama context.done() implicitamente 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 à HttpResponseSimple interface, que tem as seguintes propriedades:

Propriedade Type Description
headers Record<string, string> (opcional) Cabeçalhos de resposta HTTP.
cookies Cookie[] (opcional) Cookies de resposta HTTP.
body any (opcional) Corpo de resposta HTTP.
statusCode number (opcional) Código de status da resposta HTTP. Se não estiver definido, 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 context.res objeto sem substituí-lo. O objeto padrão context.res usa a HttpResponseFull interface, que suporta os seguintes métodos, além das HttpResponseSimple propriedades:

Método Description
status() Define o status.
setHeader() Define um campo de cabeçalho. NOTA: res.set() e res.header() também são suportados e fazem a mesma coisa.
getHeader() Obtenha um campo de cabeçalho. NOTA: res.get() também é suportado e faz a mesma coisa.
removeHeader() Remove um cabeçalho.
type() Define o cabeçalho "content-type".
send() Este método foi preterido. Ele define o corpo e as chamadas context.done() para indicar que uma função de sincronização foi concluída. NOTA: res.end() também é suportado e faz a mesma coisa.
sendStatus() Este método foi preterido. Ele define o código de status e as chamadas context.done() para indicar que uma função de sincronização foi concluída.
json() Este método foi 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: Esta opção é a maneira mais concisa de retornar respostas.

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

    A HttpResponseInit interface tem as seguintes propriedades:

    Propriedade Type Description
    body BodyInit (opcional) Corpo de resposta HTTP como um dos ArrayBuffer, , , Blob, FormData, Iterable<Uint8Array>NodeJS.ArrayBufferView, nullURLSearchParams, , ou stringAsyncIterable<Uint8Array>.
    jsonBody any (opcional) Um corpo de resposta HTTP serializável por JSON. Se definida, a HttpResponseInit.body propriedade é ignorada em favor dessa propriedade.
    status number (opcional) Código de status da resposta HTTP. Se não estiver definido, o padrão será 200.
    headers HeadersInit (opcional) Cabeçalhos de resposta HTTP.
    cookies Cookie[] (opcional) Cookies de resposta HTTP.
  • Como uma classe com tipo HttpResponse: Esta 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 HttpResponse classe aceita um opcional HttpResponseInit como um argumento para seu construtor e tem as seguintes propriedades:

    Propriedade Type Description
    status number Código de status da 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 facilita o processamento de dados grandes, o streaming de respostas OpenAI, o fornecimento de conteúdo dinâmico e o suporte a outros cenários HTTP principais. Ele permite que você transmita solicitações e respostas de pontos de extremidade HTTP em seu aplicativo de função Node.js. Use fluxos HTTP em cenários em que seu aplicativo requer troca e interação em tempo real entre cliente e servidor por HTTP. Você também pode usar fluxos HTTP para obter o melhor desempenho e confiabilidade para seus aplicativos ao usar HTTP.

Importante

Os fluxos HTTP não são suportados no modelo v3. Atualize para o modelo v4 para usar o recurso de streaming HTTP.

Os tipos existentes HttpRequest no HttpResponse modelo de programação v4 já suportam várias maneiras de lidar com o corpo da mensagem, inclusive como um fluxo.

Pré-requisitos

Ativar fluxos

Use estas etapas para habilitar fluxos HTTP em seu aplicativo de função no Azure e em seus projetos locais:

  1. Se você planeja transmitir grandes quantidades de dados, modifique a FUNCTIONS_REQUEST_BODY_SIZE_LIMIT configuração no Azure. O tamanho máximo de corpo permitido padrão é 104857600, o que limita suas solicitações a um tamanho de ~100 MB.

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

  3. Adicione o seguinte código ao seu aplicativo em qualquer arquivo incluído pelo campo principal.

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

Exemplos de fluxo

Este exemplo mostra uma função acionada por HTTP que recebe dados por meio de uma solicitação HTTP POST e 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!' };
    },
});

Este exemplo mostra uma função acionada por HTTP que transmite o conteúdo de um arquivo como resposta a solicitações HTTP GET de entrada:

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 executar usando streams, confira este exemplo no GitHub.

Considerações sobre o fluxo

  • Use request.body para obter o máximo benefício do uso de fluxos. Você ainda pode continuar a usar métodos como request.text(), que sempre retornam o corpo como uma string.

Hooks

Ganchos não são suportados no modelo v3. Atualize para o modelo v4 para usar ganchos.

Use um gancho para executar código em diferentes pontos do ciclo de vida do Azure Functions. Os ganchos são executados na ordem em que são registrados e podem ser registrados a partir de qualquer arquivo em seu aplicativo. Atualmente, existem dois escopos de ganchos, nível "app" e nível "invocação".

Ganchos de invocação

Os ganchos de invocação são executados uma vez por invocação de sua função, antes em um preInvocation gancho ou depois em um postInvocation gancho. Por padrão, seu 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 PreInvocationContext objeto tem as seguintes propriedades:

Property Description
inputs Os argumentos passaram para a invocação.
functionHandler O manipulador de função para a invocação. As alterações nesse valor afetam a própria função.
invocationContext O objeto de contexto de invocação passou para a função.
hookData O local recomendado para armazenar e compartilhar dados entre ganchos no mesmo escopo. Você deve usar um nome de propriedade exclusivo para que ele não entre em conflito com os dados de outros ganchos.

O PostInvocationContext objeto tem as seguintes propriedades:

Property Description
inputs Os argumentos passaram para a invocação.
result O resultado da função. As alterações a este valor afetam o resultado geral da função.
error O erro gerado pela função, ou null/undefined se não houver nenhum erro. As alterações a este valor afetam o resultado geral da função.
invocationContext O objeto de contexto de invocação passou para a função.
hookData O local recomendado para armazenar e compartilhar dados entre ganchos no mesmo escopo. Você deve usar 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 seu aplicativo, durante a inicialização em um appStart gancho ou durante o encerramento em um appTerminate gancho. Os ganchos de encerramento de aplicativo têm um tempo limitado para serem executados e não são executados em todos os cenários.

O tempo de execução do Azure Functions atualmente não oferece suporte ao log de contexto fora de uma invocação. Use o pacote npm do Application Insights para registrar dados durante 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 AppStartContext objeto tem as seguintes propriedades:

Property Description
hookData O local recomendado para armazenar e compartilhar dados entre ganchos no mesmo escopo. Você deve usar um nome de propriedade exclusivo para que ele não entre em conflito com os dados de outros ganchos.

O AppTerminateContext objeto tem as seguintes propriedades:

Property Description
hookData O local recomendado para armazenar e compartilhar dados entre ganchos no mesmo escopo. Você deve usar 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 necessário. O Azure Functions usa limites internos (não configuráveis pelo usuário) para diferentes tipos de gatilho para decidir quando adicionar instâncias, como a idade das mensagens e o tamanho da fila para o QueueTrigger. Para obter mais informações, consulte Como funcionam os planos Consumo e Premium.

Esse comportamento de dimensionamento é suficiente para muitos aplicativos Node.js. Para aplicativos vinculados à CPU, você pode melhorar ainda mais o desempenho usando vários processos de trabalho de idioma. 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 FUNCTIONS_WORKER_PROCESS_COUNT aplicativo. Em seguida, o Azure Functions tenta distribuir uniformemente invocações de função simultâneas entre esses trabalhadores. Esse comportamento torna menos provável que uma função com uso intensivo de CPU bloqueie a execução de outras funções. A configuração se aplica a cada host que o Azure Functions cria ao dimensionar seu aplicativo para atender à demanda.

Aviso

Use a FUNCTIONS_WORKER_PROCESS_COUNT configuração com cuidado. 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 que o tempo de execução está usando registrando em log process.version de qualquer função. Consulte supported versions para obter uma lista de Node.js versões suportadas por cada modelo de programação.

Definindo a versão do nó

A maneira como você atualiza sua versão Node.js depende do sistema operacional no qual seu aplicativo de função é executado.

Quando executado no Windows, a versão Node.js é definida pela configuração do WEBSITE_NODE_DEFAULT_VERSION aplicativo. Essa configuração pode ser atualizada usando a CLI do Azure ou no portal do Azure.

Para obter mais informações sobre Node.js versões, consulte Versões suportadas.

Antes de atualizar sua versão Node.js, verifique se seu aplicativo de função está sendo executado na versão mais recente do tempo de execução do Azure Functions. Se você precisar atualizar sua versão de tempo de execução, consulte Migrar aplicativos do Azure Functions versão 3.x para a versão 4.x.

Execute o comando CLI az functionapp config appsettings set do Azure para atualizar a versão Node.js para seu aplicativo de função 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 configuração do WEBSITE_NODE_DEFAULT_VERSION aplicativo como a versão LTS suportada do ~20.

Depois que as alterações forem feitas, seu aplicativo de função será reiniciado. Para saber mais sobre o suporte de funções para Node.js, consulte Política de suporte de tempo de execução 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 criação de perfil. Você pode adicionar variáveis de ambiente em seus ambientes locais e de nuvem e acessá-las através do process.env seu código de função.

O exemplo a seguir registra a WEBSITE_SITE_NAME variável de ambiente:

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"]}`);
}

Em 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 Values ambiente no objeto.

{
  "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ção permite definir e usar 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 as configurações do aplicativo de função:

As alterações nas configurações do aplicativo de função exigem que seu aplicativo de função seja reiniciado.

Variáveis do ambiente de trabalho

Existem várias variáveis de ambiente Functions específicas para Node.js:

languageWorkers__node__arguments

Essa configuração permite especificar argumentos personalizados ao iniciar o processo de Node.js. É usado com mais frequência localmente para iniciar o trabalhador 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 porque isso pode ter um efeito negativo nos tempos de inicialização a frio. Em vez de usar trabalhadores pré-aquecidos, o tempo de execução tem que iniciar um novo trabalhador do zero com seus argumentos personalizados.

logging__logLevel__Worker

Essa configuração ajusta o nível de log padrão para 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-los para 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 (pré-visualização)

Nota

Como os módulos ECMAScript são atualmente um recurso de visualização no Node.js 14 ou superior no Azure Functions.

Os módulos ECMAScript (módulos ES) são o novo sistema de módulos padrão oficial 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 superior, você pode optar por escrever suas funções usando a sintaxe dos módulos ES.

Para usar módulos ES em uma função, altere seu nome de arquivo para usar uma .mjs extensão. O exemplo de arquivo index.mjs a seguir é uma função acionada por HTTP que usa a sintaxe dos módulos ES para importar a uuid biblioteca 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 ponto de entrada de função

As function.json propriedades scriptFile e entryPoint podem ser usadas para configurar o local e o nome da função exportada. A scriptFile propriedade é necessária quando você está usando TypeScript e deve apontar para o JavaScript compilado.

Ao utilizar scriptFile

Por padrão, uma função JavaScript é executada a partir de index.js, um arquivo que compartilha o mesmo diretório pai que seu diretório correspondente function.json.

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 for myFirstFunction deve incluir uma scriptFile propriedade apontando para o arquivo com a função exportada a ser executada.

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

Ao utilizar 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 acionada é a única exportação desse arquivo, a exportação nomeada runou a exportação nomeada index. O exemplo a function.json seguir define entryPoint um valor personalizado, "logHello":

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

module.exports = { logHello };

Depuração local

É recomendável usar o VS Code para depuração local, que inicia seu processo de Node.js no modo de depuração automaticamente e se anexa ao processo para você. Para obter mais informações, consulte Executar a função localmente.

Se você estiver usando uma ferramenta diferente para depuração ou quiser iniciar seu processo de Node.js no modo de depuração manualmente, adicione "languageWorkers__node__arguments": "--inspect" em Values seu local.settings.json. O --inspect argumento diz a 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 de Node.js.

Recomendações

Esta seção descreve vários padrões impactantes para Node.js aplicativos que recomendamos que você siga.

Escolha planos de Serviço de Aplicativo de vCPU única

Quando você cria um aplicativo de função que usa o plano do Serviço de Aplicativo, recomendamos que você selecione um plano de vCPU única em vez de um plano com várias vCPUs. Atualmente, o Functions é executado Node.js funções de forma mais eficiente em VMs de vCPU única, e o uso de VMs maiores não produz as melhorias de desempenho esperadas. Quando necessário, você pode expandir manualmente adicionando mais instâncias de VM de vCPU única ou habilitar o dimensionamento automático. Para obter mais informações, consulte Dimensionar a contagem de instâncias manualmente ou automaticamente.

Executar a partir de um arquivo de pacote

Quando você desenvolve o Azure Functions no modelo de hospedagem sem servidor, os arranques a frio são uma realidade. Arranque a frio refere-se à primeira vez que a sua aplicação funcional é iniciada após um período de inatividade, demorando mais tempo a arrancar. Para aplicativos Node.js com grandes árvores de dependência em particular, o arranque a frio pode ser significativo. Para acelerar o processo de arranque a frio, execute as suas funções como um ficheiro de pacote sempre que possível. Muitos métodos de implantação usam esse modelo por padrão, mas se você estiver enfrentando grandes partidas a frio, verifique se está executando dessa maneira.

Usar um único cliente estático

Quando você usa um cliente específico de serviço em um aplicativo do Azure Functions, não crie um novo cliente com cada invocação de função porque você pode atingir limites de conexão. Em vez disso, crie um único cliente estático no escopo global. Para obter mais informações, consulte Gerenciando conexões no Azure Functions.

Utilização async e await

Ao escrever o Azure Functions no Node.js, você deve escrever código usando as async palavras-chave e await . Escrever código usando async e await em vez de retornos de chamada ou .then e .catch com Promessas ajuda a evitar dois problemas comuns:

  • Lançar exceções não detetadas que travam o processo de Node.js, potencialmente afetando a execução de outras funções.
  • Comportamento inesperado, como logs ausentes do context.log, causados por chamadas assíncronas que não são esperadas corretamente.

No exemplo a seguir, o método fs.readFile assíncrono é invocado com uma função de retorno de chamada error-first como seu segundo parâmetro. Este código causa ambos os problemas mencionados anteriormente. Uma exceção que não é explicitamente capturada no escopo correto pode travar todo o processo (problema #1). Retornar sem garantir que o retorno de chamada termine significa que a resposta http às vezes terá um corpo vazio (problema #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 exemplo a seguir, o método fs.readFile assíncrono é invocado com uma função de retorno de chamada error-first como seu segundo parâmetro. Este código causa ambos os problemas mencionados anteriormente. Uma exceção que não é explicitamente capturada no escopo correto pode travar todo o processo (problema #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 seja 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 async palavras-chave 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, Node.js fornece uma fs/promises API para substituir a API de retorno de fs chamada.

No exemplo a seguir, quaisquer exceções não tratadas lançadas durante a execução da função só falham a invocação individual que gerou a exceção. A await palavra-chave significa que as etapas seguintes readFile só são executadas depois de 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 ligar para o context.done() retorno de chamada.

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

Resolver problemas

Consulte o Guia de solução de problemas do Node.js.

Próximos passos

Para obter mais informações, consulte os seguintes recursos: