Azure Functions

Output Bindings: Azure Table Storage

Output bindings for Azure Table Storage allow your Azure Functions to write data to a table without needing to write explicit SDK code. This simplifies your function code and reduces boilerplate. You can define an output binding that targets a specific table, and then return values or use a context object to insert rows.

When to Use Table Output Bindings

Use table output bindings when you need to:

  • Persist small amounts of structured data.
  • Store metadata or configuration settings.
  • Log events or telemetry data.
  • Integrate with other Azure services that read from Table Storage.

Configuration

The table output binding is configured in your function's function.json file. Here's a typical configuration:

{
  "bindings": [
    {
      "name": "outputTable",
      "type": "table",
      "direction": "out",
      "tableName": "MyOutputTable",
      "connection": "AzureWebJobsStorage"
    }
  ],
  "scriptFile": "run.csx"
}

Key properties:

  • name: The name of the variable that represents the output binding in your function code.
  • type: Must be "table".
  • direction: Must be "out".
  • tableName: The name of the Azure Table Storage table to write to. This table will be created if it doesn't exist.
  • connection: The name of an app setting that contains the Azure Table Storage connection string. Often, this is set to "AzureWebJobsStorage", which refers to the default storage account for your Azure Function app.

Using the Binding in Code

C# Example

In C#, you can return a single object or a collection of objects. The function runtime will automatically handle inserting/upserting them into the specified table.

using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;

public static class TableOutputTrigger
{
    [FunctionName("WriteToTable")]
    public static void Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)]
        Microsoft.HttpRequest req,
        [Table("MyOutputTable", Connection = "AzureWebJobsStorage")] IAsyncCollector outputTable,
        ILogger log)
    {
        log.LogInformation("C# HTTP trigger function processed a request.");

        var row = new MyTableRow
        {
            PartitionKey = "General",
            RowKey = Guid.NewGuid().ToString(),
            Message = "Hello from Azure Functions!"
        };

        outputTable.AddAsync(row).Wait();
        log.LogInformation($"Row added to MyOutputTable with RowKey: {row.RowKey}");
    }
}

public class MyTableRow
{
    public string PartitionKey { get; set; }
    public string RowKey { get; set; }
    public string Message { get; set; }
}

JavaScript Example (Node.js)

In JavaScript, you'll use the context.bindings object to set the output.

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

    const rowData = {
        PartitionKey: 'Web',
        RowKey: Date.now().toString(),
        Message: 'Data from Node.js'
    };

    context.bindings.outputTable = rowData;

    context.log(`Row added to MyOutputTable with RowKey: ${rowData.RowKey}`);
};

Python Example

In Python, you can pass a dictionary or a list of dictionaries to the output binding.

import logging
import uuid

import azure.functions as func

def main(req: func.HttpRequest, outputTable: func.Out[func.Document]) -> None:
    logging.info('Python HTTP trigger function processed a request.')

    row_key = str(uuid.uuid4())
    data = {
        'PartitionKey': 'Python',
        'RowKey': row_key,
        'Message': 'Hello from Python Functions!'
    }

    outputTable.set(data)

    logging.info(f'Row added to MyOutputTable with RowKey: {row_key}')

Row Keys and Partition Keys

When writing to Table Storage, each row must have a PartitionKey and a RowKey. The output binding will automatically set these if they are present in the object you provide. If they are not present, you will need to provide them:

  • PartitionKey: Rows with the same PartitionKey are grouped together on the same storage partition. This helps with query performance and scalability.
  • RowKey: The RowKey uniquely identifies a row within a PartitionKey. Together, PartitionKey and RowKey form the primary key.

For simple scenarios, you can use a static PartitionKey (e.g., "General") and a unique RowKey (e.g., a GUID or timestamp).

Handling Collections

You can also send multiple rows at once. In C#, you can use an IEnumerable<T> or IAsyncCollector<T> with multiple AddAsync calls. In Node.js and Python, you can assign an array of objects to the output binding.

JavaScript (Node.js) - Sending an array

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

    const rows = [
        { PartitionKey: 'Batch', RowKey: '1', Message: 'First item' },
        { PartitionKey: 'Batch', RowKey: '2', Message: 'Second item' }
    ];

    context.bindings.outputTable = rows; // Assign the array

    context.log(`Batch of ${rows.length} rows added to MyOutputTable.`);
};

Error Handling

The Azure Functions runtime handles the actual Table Storage operations. If there are transient errors during the write operation, the runtime might retry. For persistent errors, the function execution will likely fail. Ensure your connection strings are correctly configured in your app settings.

Limitations

While convenient, output bindings have some limitations:

  • Less control over advanced Table Storage features like batch operations for large numbers of entities or specific error handling strategies.
  • For complex scenarios or very high throughput, consider using the Azure Storage SDK directly within your function.