← Back to ArticlesFunctions

Functions — Input/Output Bindings for Simplified Data Access

Using Azure Functions input and output bindings to read/write to Azure services without writing integration code.

Functions — Input/Output Bindings for Simplified Data Access

Overview

Azure Functions bindings provide a declarative way to connect to Azure services without writing integration code. Input bindings let you read data into your function, and output bindings let you write data out. This dramatically simplifies your code - instead of managing connections and SDKs, you just work with parameters.

What You'll Learn


Problem

Your functions contain:


Solution - Input Bindings

Blob Input Binding

public static class BlobInputFunction
{
    [FunctionName("ReadBlob")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
        [Blob("container/{filename}")] string blobContent,
        ILogger log)
    {
        log.LogInformation($"Read blob content: {blobContent}");
        
        return new OkObjectResult(new 
        { 
            content = blobContent,
            length = blobContent?.Length ?? 0
        });
    }
}

Queue Input Binding

public static class QueueInputFunction
{
    [FunctionName("ProcessQueueMessage")]
    public static void Run(
        [QueueTrigger("orders")] string orderMessage,
        ILogger log)
    {
        log.LogInformation($"Processing order: {orderMessage}");
        
        var order = JsonSerializer.Deserialize<Order>(orderMessage);
        // Process order...
    }
}

Table Input Binding

public static class TableInputFunction
{
    [FunctionName("GetFromTable")]
    public static IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Function, "get", Route = "customers/{partitionKey}/{rowKey}")] HttpRequest req,
        [Table("customers", "{partitionKey}", "{rowKey}")] Customer customer,
        ILogger log)
    {
        if (customer == null)
            return new NotFoundResult();
            
        return new OkObjectResult(customer);
    }
}

Solution - Output Bindings

Blob Output Binding

public static class BlobOutputFunction
{
    [FunctionName("WriteToBlob")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
        [Blob("output/{filename}", FileAccess.Write)] Stream outputBlob,
        ILogger log)
    {
        var content = await new StreamReader(req.Body).ReadToEndAsync();
        
        // Simply write - binding handles the rest
        var bytes = Encoding.UTF8.GetBytes(content);
        await outputBlob.WriteAsync(bytes, 0, bytes.Length);
        
        return new OkObjectResult(new { written = true });
    }
}

Queue Output Binding

public static class QueueOutputFunction
{
    [FunctionName("SendToQueue")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
        [Queue("output-orders")] ICollector<string> outputQueue,
        ILogger log)
    {
        var order = await req.ReadAsJsonAsync<Order>();
        
        // Add to queue - binding handles serialization
        outputQueue.Add(JsonSerializer.Serialize(order));
        
        return new AcceptedResult();
    }
}

Table Output Binding

public static class TableOutputFunction
{
    [FunctionName("WriteToTable")]
    public static IActionResult Run(
        [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
        [Table("audit")] ICollector<AuditRecord> tableBinding,
        ILogger log)
    {
        var input = req.ReadFromJsonAsync<InputData>();
        
        // Add to table
        tableBinding.Add(new AuditRecord
        {
            PartitionKey = "audit",
            RowKey = Guid.NewGuid().ToString(),
            Timestamp = DateTime.UtcNow,
            Action = input.action,
            Details = input.details
        });
        
        return new OkObjectResult(new { saved = true });
    }
}

public class AuditRecord
{
    public string PartitionKey { get; set; }
    public string RowKey { get; set; }
    public DateTime Timestamp { get; set; }
    public string Action { get; set; }
    public string Details { get; set; }
}

Combined Input/Output Bindings

Process and Store Pattern

public static class ProcessAndStoreFunction
{
    [FunctionName("ProcessAndStore")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequest req,
        [Blob("input/{filename}")] Stream inputBlob,
        [Blob("output/processed-{filename}")] Stream outputBlob,
        ILogger log)
    {
        // Read from input blob
        using var reader = new StreamReader(inputBlob);
        var content = await reader.ReadToEndAsync();
        
        // Process data
        var processed = ProcessData(content);
        
        // Write to output blob
        var bytes = Encoding.UTF8.GetBytes(processed);
        await outputBlob.WriteAsync(bytes, 0, bytes.Length);
        
        return new OkObjectResult(new { processed = true });
    }
    
    private string ProcessData(string data)
    {
        // Transform data
        return data.ToUpper();
    }
}

Real-Time Scenarios

Scenario 1: Image Processing Pipeline

public static class ImageProcessingFunction
{
    [FunctionName("ProcessImage")]
    public static async Task Run(
        [BlobTrigger("uploads/{name}")] Stream inputImage,
        [Blob("uploads/{name}")] string imageName,
        [Blob("thumbnails")] BlobContainerClient thumbContainer,
        [Queue("thumbnail-jobs")] ICollector<ThumbnailJob> queue,
        ILogger log)
    {
        // Load and resize image
        using var input = Image.Load(inputImage);
        input.Mutate(x => x.Resize(new ResizeOptions 
        { 
            Size = new Size(200, 200),
            Mode = ResizeMode.Crop 
        }));
        
        // Save thumbnail via binding
        var thumbBlob = thumbContainer.GetBlobClient($"thumb_{imageName}");
        
        using var ms = new MemoryStream();
        input.SaveAsJpeg(ms);
        ms.Position = 0;
        
        await thumbBlob.UploadAsync(ms);
        
        // Also queue notification
        queue.Add(new ThumbnailJob 
        { 
            OriginalName = imageName,
            ThumbUrl = thumbBlob.Uri.ToString()
        });
        
        log.LogInformation("Processed image: {Name}", imageName);
    }
}

Scenario 2: Data Import with Validation

public static class DataImportFunction
{
    [FunctionName("ImportData")]
    public static async Task<IActionResult> Run(
        [BlobTrigger("import/{filename}.csv")] string csvContent,
        [Table("import-records")] ICollector<ImportRecord> table,
        [Queue("import-errors")] ICollector<ImportError> errorQueue,
        ILogger log)
    {
        var lines = csvContent.Split('\n');
        var imported = 0;
        var errors = 0;
        
        for (int i = 1; i < lines.Length; i++)
        {
            try
            {
                var record = ParseLine(lines[i]);
                
                if (Validate(record))
                {
                    table.Add(new ImportRecord
                    {
                        PartitionKey = DateTime.UtcNow.ToString("yyyy-MM-dd"),
                        RowKey = Guid.NewGuid().ToString(),
                        Data = JsonSerializer.Serialize(record),
                        ImportedAt = DateTime.UtcNow
                    });
                    imported++;
                }
                else
                {
                    errors++;
                }
            }
            catch (Exception ex)
            {
                errorQueue.Add(new ImportError
                {
                    LineNumber = i,
                    Error = ex.Message,
                    Content = lines[i]
                });
                errors++;
            }
        }
        
        return new OkObjectResult(new { imported, errors });
    }
}

Scenario 3: Webhook Processing

public static class WebhookFunction
{
    [FunctionName("HandleWebhook")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req,
        [Table("webhooks")] ICollector<WebhookEvent> table,
        [ServiceBus("webhook-events")] ICollector<string> serviceBus,
        ILogger log)
    {
        var payload = await req.ReadAsStringAsync();
        var eventData = JsonSerializer.Deserialize<WebhookPayload>(payload);
        
        // Store in table
        table.Add(new WebhookEvent
        {
            PartitionKey = eventData.EventType,
            RowKey = eventData.EventId,
            Timestamp = DateTime.UtcNow,
            Source = eventData.Source,
            Data = payload
        });
        
        // Publish to Service Bus for async processing
        serviceBus.Add(JsonSerializer.Serialize(eventData));
        
        return new OkResult();
    }
}

Binding Expressions

Using Route Parameters

[FunctionName("GetFile")]
public static IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "files/{container}/{filename}")] HttpRequest req,
    [Blob("{container}/{filename}")] string content,
    ILogger log)
{
    return new OkObjectResult(content);
}

Using Query Parameters

[FunctionName("GetByQuery")]
public static IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
    [Blob("files/{Query.filename}")] string content,
    ILogger log)
{
    return new OkObjectResult(content);
}

System Variables

[FunctionName("SystemInfo")]
public static IActionResult Run(
    [HttpTrigger(AuthorizationLevel.Function, "get")] HttpRequest req,
    [Blob("logs/{Date}.txt", FileAccess.Write)] Stream logFile,
    ILogger log)
{
    var info = $"Processed at {DateTime.UtcNow} by function {context.FunctionName}";
    return new OkObjectResult(info);
}

Best Practices

PracticeWhy
Use bindings for Azure servicesReduces boilerplate code
Use proper types (Stream vs string)Better performance
Handle missing data gracefullyReturn 404 for missing blobs
Use connection strings in configEnvironment-specific settings

Summary