googleGoogle Cloud Functions

Run BoxLang on Google Cloud Functions with the official starter and runtime

Run BoxLang handlers on Google Cloud Functions Gen 2 using the Java 21 runtime and the BoxLang GCF bridge entry point: ortus.boxlang.runtime.gcp.FunctionRunner.

This page matches the current starter project and runtime behavior.

🚀 What You Get

  • BoxLang handler files in src/main/bx

  • Convention-based routing from URI to handler class

  • Local HTTP server via the Google Functions Java Invoker

  • Deployable ZIP with handlers, config, modules, and runtime JAR

🏛️ Runtime + Starter Architecture

This experience is intentionally a combination of two projects:

  • Starter project (boxlang-starter-google-functions): your app shell, handlers, tests, and Gradle tasks

  • Runtime project (boxlang-google-functions): the Java bridge that adapts GCF HTTP requests to BoxLang execution

The runtime entry point is:

  • ortus.boxlang.runtime.gcp.FunctionRunner

At execution time, the runtime handles:

  • HTTP request mapping into a BoxLang-friendly event struct

  • route resolution from URI to .bx handler file

  • class compilation/loading and warm-invocation class caching

  • method dispatch (default run() or x-bx-function override)

  • response mapping from BoxLang response struct to GCF HTTP output

This split lets you keep all business logic in BoxLang files while using Java only as the serverless runtime bridge.

🔬 Runtime Execution Flow

⚡ Cold Start, Warm Start, and Debug Mode

  • Cold start: runtime initializes in the container before first request handling.

  • Warm invocations: compiled handler classes are reused to avoid repeated compilation work.

  • Debug mode (BOXLANG_GCP_DEBUGMODE=true): class caching is disabled so handler edits are picked up quickly during development.

  • Production guidance: keep debug mode off for best performance.

🔁 Portability Notes

The runtime keeps the handler contract familiar across serverless targets. In practice, many handler patterns can be shared between BoxLang AWS Lambda and BoxLang GCF with minimal changes.

✅ Requirements

Requirement
Version

Java

21

Google Cloud SDK (gcloud)

Latest

Gradle

Wrapper included

📦 Starter Project Setup

Use the official starter:

Run tests once to verify your environment:

🧪 Launch Locally

Start the local function server:

By default it runs on port 9099 and uses src/main/bx as the function root.

Try a request:

Call a specific method via header:

Send JSON payload:

Local overrides

Local runFunction Runner Details

The starter's runFunction Gradle task uses the official Google Functions Java Invoker and wires it to the BoxLang runtime entry point.

What it does:

  • launches com.google.cloud.functions.invoker.runner.Invoker

  • passes ortus.boxlang.runtime.gcp.FunctionRunner as the target

  • sets BOXLANG_GCP_ROOT to your function root (default: src/main/bx)

  • sets BOXLANG_GCP_DEBUGMODE from -PdebugMode or defaults

  • runs on -PtestPort (default 9099)

Expected startup banner:

Runner troubleshooting:

  • Port in use: start with -PtestPort=8080

  • Wrong handlers loaded: point to the right folder with -PfunctionRoot=...

  • Code changes not reflected: use -PdebugMode=true during development

  • Missing default route: ensure Lambda.bx exists in the configured function root

🧩 Handler Contract

Each handler method receives:

  • event: HTTP request data mapped into a struct

  • context: function metadata (name, revision, project, request id)

  • response: mutable response struct (statusCode, headers, body, cookies)

Default method is run().

Event Struct Shape

The runtime maps each incoming HTTP request into an event struct designed for serverless portability.

Context Struct Shape

The context struct contains runtime metadata:

  • functionName

  • functionVersion

  • projectId

  • requestId

Response Struct Shape

The runtime provides a mutable response struct:

Response behavior:

  • Returning a struct or array from your method will be JSON serialized.

  • Returning a plain string writes that string as the response body.

  • Writing to response.body gives explicit control over output.

Example handler

🛣️ Convention-Based Routing

The runtime supports multi-routing by resolving the first URI path segment into a PascalCase .bx handler file.

Routing algorithm:

  1. Read the first path segment from the request URI.

  2. Convert it to PascalCase.

  3. Look for <Segment>.bx under BOXLANG_GCP_ROOT.

  4. Fall back to Lambda.bx if no file is found.

URI to Handler Mapping

Request URI
Resolved Handler

/

Lambda.bx

/customers

Customers.bx

/customers/123

Customers.bx

/products

Products.bx

/user-profiles

UserProfiles.bx

/api_endpoints

ApiEndpoints.bx

/unknown

Lambda.bx

Multi-Handler Layout

The first segment selects the class. Remaining URI segments are still available via event.path for your own parsing.

Multi-Routing Handler Example

🔀 Method Routing with x-bx-function

After URI routing resolves the handler class, method routing can choose which function to invoke.

  • Without header, runtime calls run().

  • With header, runtime attempts the named method.

This gives you two dispatch layers:

  • URI path selects the handler class

  • x-bx-function selects the method in that class

⚙️ Runtime Environment Variables

Variable
Purpose

BOXLANG_GCP_ROOT

Root directory for .bx handlers

BOXLANG_GCP_CLASS

Override default handler path

BOXLANG_GCP_DEBUGMODE

Enable verbose logging and disable class caching

BOXLANG_GCP_CONFIG

Custom boxlang.json path

K_SERVICE

Function name (set by GCF)

K_REVISION

Function revision (set by GCF)

GOOGLE_CLOUD_PROJECT

Project ID (set by GCF)

🏗️ Build Deployable Artifacts

Build the GCF package:

Output ZIP:

  • build/distributions/boxlang-google-function-project-<version>.zip

The ZIP includes:

  • .bx handlers at ZIP root

  • boxlang.json

  • boxlang_modules/

  • lib/ with runtime and dependencies

☁️ Deploy to Google Cloud Functions Gen 2

Authenticate and select project:

Deploy:

If you changed version in gradle.properties, update the ZIP filename in --source.

🧪 Testing

Run tests:

Run the integration test class:

Open report:

  • build/reports/tests/test/index.html

Unit and Integration Tests Shipped in the Starter

The starter currently ships one primary integration test class and two GCF HTTP mock classes:

File
Purpose

src/test/java/com/myproject/FunctionRunnerTest.java

End-to-end tests of request -> BoxLang handler -> HTTP response

src/test/java/com/myproject/mocks/MockHttpRequest.java

Fluent test request builder implementing GCF HttpRequest

src/test/java/com/myproject/mocks/MockHttpResponse.java

Captures status/body/headers implementing GCF HttpResponse

FunctionRunnerTest validates:

  • missing handler path error behavior

  • default run() execution and JSON response assertions

  • x-bx-function method routing (anotherLambda)

  • response headers (Content-Type)

  • empty headers and large-body handling

  • query parameter handling

  • concurrent invocation behavior

  • core accessor behavior (getDefaultFunctionPath, inDebugMode, getRuntime)

Run only the shipped integration test:

📝 Notes

  • The starter currently keeps handlers in src/main/bx, not src/main/resources.

  • Local launch uses -PtestPort and -PdebugMode flags.

  • For this starter, deployment source is the generated ZIP in build/distributions.

Last updated

Was this helpful?