Getting Started: Benzene on Google Cloud Functions

This guide takes you from an empty folder to a Benzene service running on Google Cloud Functions (Gen2). The handler you write is identical to every other host — only the entry point and deploy command are Google-specific. Deploying somewhere else? See AWS Lambda, Azure Functions, or the platform picker.

Runnable version: this guide follows examples/Google — the same Startup hosted on both Cloud Functions Gen2 (Function.cs) and Cloud Run (Program.cs).

What you'll build

An HTTP-triggered function that handles POST /orders and returns a JSON response — the same transport-agnostic handler you'd deploy to any host.

Prerequisites

1. Create the project

mkdir OrdersFunction && cd OrdersFunction
dotnet new console -f net10.0

2. Install the Benzene package

Benzene's packages are prerelease (-alpha) until 1.0, so --prerelease is required:

dotnet add package Benzene.GoogleCloud.Functions.Http --prerelease

Benzene.GoogleCloud.Functions.Http brings the Google Cloud Functions Framework host plus Benzene's HTTP pipeline and message-handler infrastructure transitively.

3. Write a message handler

Create PlaceOrderMessageHandler.cs. This file is identical to what you'd write for AWS or ASP.NET — it knows nothing about Google Cloud:

using Benzene.Abstractions.MessageHandlers;
using Benzene.Abstractions.Results;
using Benzene.Core.MessageHandlers;
using Benzene.Http;
using Benzene.Results;

namespace OrdersFunction;

[Message("order:placed")]
[HttpEndpoint("POST", "/orders")]
public class PlaceOrderMessageHandler : IMessageHandler<OrderPlaced, OrderAccepted>
{
    public Task<IBenzeneResult<OrderAccepted>> HandleAsync(OrderPlaced message)
    {
        var response = new OrderAccepted { OrderId = message.OrderId, Status = "accepted" };
        return Task.FromResult(BenzeneResult.Ok(response));
    }
}

public class OrderPlaced { public string OrderId { get; set; } public string Customer { get; set; } }
public class OrderAccepted { public string OrderId { get; set; } public string Status { get; set; } }

4. Define the StartUp and the function entry point

StartUp.cs is the platform-neutral BenzeneStartUp — the same shape every host uses:

using Benzene.Abstractions.Hosting;
using Benzene.AspNet.Core;
using Benzene.Core.MessageHandlers;
using Benzene.Microsoft.Dependencies;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace OrdersFunction;

public class Startup : BenzeneStartUp
{
    public override IConfiguration GetConfiguration()
        => new ConfigurationBuilder().AddEnvironmentVariables().Build();

    public override void ConfigureServices(IServiceCollection services, IConfiguration configuration)
    {
        // Nothing Benzene-specific to register - register your own services here. UseHttp and
        // UseMessageHandlers in Configure wire up the HTTP mappers and discover your handlers.
    }

    public override void Configure(IBenzeneApplicationBuilder app, IConfiguration configuration)
        => app.UseHttp(http => http.UseMessageHandlers());
}

ConfigureServices registers no Benzene services: UseHttp(...) adds the HTTP request/response mappers and UseMessageHandlers() discovers your [Message]/[HttpEndpoint] handlers by reflection and pulls in the core pipeline for you. Pass an assembly or type list to UseMessageHandlers(...) if you'd rather scope discovery explicitly.

Function.cs is the only Google-specific line — it hosts that Startup on the Functions Framework:

using Benzene.GoogleCloud.Functions.Http;

namespace OrdersFunction;

public class Function : GoogleCloudFunctionHost<Startup> { }

5. Deploy

gcloud functions deploy orders \
  --gen2 --runtime dotnet10 --region europe-west2 \
  --source . --entry-point OrdersFunction.Function \
  --trigger-http --allow-unauthenticated

--entry-point points at your Function class; the Functions Framework does the rest. When it finishes it prints the function URL:

curl -X POST "$(gcloud functions describe orders --gen2 --region europe-west2 --format 'value(serviceConfig.uri)')/orders" \
  -H "Content-Type: application/json" -d '{"orderId":"ORD-1","customer":"acme"}'
{"orderId":"ORD-1","status":"accepted"}

Beyond HTTP: Pub/Sub

The same Startup and handlers are reachable over Pub/Sub as well — a Pub/Sub-triggered function binds an inbox topic and routes by the Benzene topic carried in the message. The Google Cloud Mesh example wires HTTP and Pub/Sub functions side by side over one shared domain; see its README for the gcloud functions deploy --trigger-topic form.

Next steps