Skip to main content

HTTP Cloud Function Routing

Based on the @cloudbase/functions-framework framework, you can split a large function into multiple sub-functions and route different requests to different handler functions through request paths

Why Function Routing is Needed

In traditional cloud function development models, each function needs to be deployed and run independently, which brings the following problems:

  • Resource Waste: Each function occupies a dedicated instance and needs to keep running even with small request volumes
  • Frequent Cold Starts: More functions mean more cold start instances
  • Complex Deployment: Need to deploy and manage multiple functions separately
  • Higher Costs: More instances lead to higher resource costs

Using function routing can solve these problems:

  • Resource Sharing: Multiple functions share the same instance, fully utilizing computing resources
  • Cost Reduction: Reduce instance count and cold start frequency
  • Simplified Deployment: Deploy multiple functions at once with unified management
  • Performance Improvement: Share resources like memory and connection pools to improve response speed

Prerequisites

Using function routing requires installing the @cloudbase/functions-framework dependency:

npm install @cloudbase/functions-framework

This framework provides core capabilities such as function loading, route distribution, and request handling.

Quick Start

Step 1: Create Project Structure

Create a project directory that supports multiple functions:

my-functions/
├── package.json # Project configuration
├── scf_bootstrap # Bootstrap script
├── cloudbase-functions.json # Function routing configuration (key)
├── index.js # Default function entry
├── user/ # user function directory
│ └── index.js
└── order/ # order function directory
└── index.js

Step 2: Create Function Code

Default Function (index.js):

exports.main = function (event, context) {
return {
message: 'Hello from default function',
path: context.httpContext.url,
};
};

user Function (user/index.js):

exports.main = function (event, context) {
return {
message: 'User API',
path: context.httpContext.url,
method: context.httpContext.httpMethod,
};
};

order Function (order/index.js):

exports.main = function (event, context) {
return {
message: 'Order API',
path: context.httpContext.url,
};
};

Step 3: Configure Function Routing

Create a cloudbase-functions.json configuration file:

{
"functionsRoot": ".",
"functions": [
{
"name": "default",
"directory": "."
},
{
"name": "user",
"directory": "user",
"triggerPath": "/api/user" // triggerPath is the simplified write method of routes, which can directly specify the trigger path in the function definition
},
{
"name": "order",
"directory": "order",
"triggerPath": "/api/order"
}
],
"routes": [
{
"functionName": "echo",
"path": "/echo"
}
]
}

Step 4: Configure package.json

{
"name": "my-functions",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"@cloudbase/functions-framework": "latest"
}
}

Step 5: Create Bootstrap Script

Create a scf_bootstrap file (without extension):

#!/bin/bash
node node_modules/@cloudbase/functions-framework/bin/tcb-ff.js \
--port=9000 \
--logDirname=/tmp/logs \
--interceptOutput=false \
--extendedContextKey=X-Cloudbase-Context \
--functionsConfigFile=cloudbase-functions.json

⚠️ Important: The scf_bootstrap file needs to have executable permissions. On Linux/macOS systems, use the chmod +x scf_bootstrap command to set permissions.

Bootstrap Parameters:

ParameterDescriptionDefault Value
--portFunction listening port9000
--logDirnameLog storage directory/tmp/logs
--interceptOutputWhether to intercept standard outputfalse
--extendedContextKeyExtended context request header key nameX-Cloudbase-Context
--functionsConfigFileFunction routing configuration file pathcloudbase-functions.json

Step 6: Install Dependencies

npm install

Step 7: Local Testing

Start the function service:

./scf_bootstrap

Test different routes:

# Test default function
curl http://localhost:9000/

# Test user function
curl http://localhost:9000/api/user

# Test order function
curl http://localhost:9000/api/order

Step 8: Deploy to Cloud

Deploy the function using CloudBase CLI or console, then access after deployment:

# Default function
https://your-function.run.tcloudbase.com/

# user function
https://your-function.run.tcloudbase.com/api/user

# order function
https://your-function.run.tcloudbase.com/api/order

Route Matching Rules

Understanding route matching rules is crucial for correctly configuring function routing:

1. Prefix Matching

Route matching uses prefix matching mode. When a request path starts with a route rule's path, it can match that route.

Example:

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
}
]
}

The following request paths will all match the user function:

  • /api/user
  • /api/user/
  • /api/user/profile
  • /api/user/list

The following request paths will not match:

  • /api/users ❌ (does not start with /api/user)
  • /api ❌ (path incomplete)

2. Longest Match

When multiple route rules can match the same request path, the framework will select the longest route rule.

Example:

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
},
{
"functionName": "userProfile",
"path": "/api/user/profile"
}
]
}

Routing results:

  • /api/useruser function
  • /api/user/listuser function
  • /api/user/profileuserProfile function (longest match)
  • /api/user/profile/detailuserProfile function (longest match)

3. Trailing Slash Handling

The trailing / in paths does not affect matching results; /api/user and /api/user/ are considered equivalent.

Example:

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
}
]
}

The following paths will all match:

  • /api/user
  • /api/user/

4. Path Unit Exact Matching

Route matching splits paths by / into path units, and each path unit requires exact matching.

Example:

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
}
]
}

Routing results:

  • /api/user
  • /api/users ❌ (users does not match user)
  • /api/user123 ❌ (user123 does not match user)

5. One Function Multiple Routes

A function can be configured with multiple trigger paths, accessing the same function from different URL paths.

Example:

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
},
{
"functionName": "user",
"path": "/v1/user"
},
{
"functionName": "user",
"path": "/v2/user"
}
]
}

All three paths above will route to the user function.

6. One Route One Function

The same path can only point to one function and cannot point to multiple functions simultaneously.

Error Example:

{
"routes": [
{
"functionName": "userA",
"path": "/api/user"
},
{
"functionName": "userB",
"path": "/api/user"
}
]
}

⚠️ Note: The above configuration will cause route conflicts and the framework will report an error.

7. Default Route

You can configure a route with path as / as the default route to handle all unmatched requests.

Example:

{
"routes": [
{
"functionName": "user",
"path": "/api/user"
},
{
"functionName": "order",
"path": "/api/order"
},
{
"functionName": "default",
"path": "/"
}
]
}

Routing results:

  • /api/useruser function
  • /api/orderorder function
  • /default function
  • /aboutdefault function
  • /healthdefault function

Advanced Usage

Using triggerPath to Simplify Configuration

triggerPath is a simplified version of routes, allowing you to specify the trigger path directly in the function definition.

Comparison:

Using triggerPath:

{
"functions": [
{
"name": "user",
"directory": "user",
"triggerPath": "/api/user"
}
],
"routes": []
}

Equivalent to:

{
"functions": [
{
"name": "user",
"directory": "user"
}
],
"routes": [
{
"functionName": "user",
"path": "/api/user"
}
]
}

💡 Tip: It's recommended to use triggerPath for more concise configuration. If you need one function to correspond to multiple routes, use routes configuration.

Combining with Express/Koa Frameworks

Function routing can be combined with Express, Koa and other web frameworks, where each sub-function can be a complete web application.

Example:

// user/index.js
const express = require('express');
const app = express();

app.get('/profile', (req, res) => {
res.json({ user: 'profile' });
});

app.get('/list', (req, res) => {
res.json({ users: [] });
});

exports.main = app;

Access:

  • https://your-function.api.tcloudbasegateway.com/v1/functions/{function-name}/api/user/profile?webfn=true
  • https://your-function.api.tcloudbasegateway.com/v1/functions/{function-name}/api/user/list?webfn=true

Mixing SSE and WebSocket

Different sub-functions can handle different types of requests, such as one function handling HTTP requests and another handling WebSocket connections.

Example:

.
├── cloudbase-functions.json
├── index.js # HTTP function
└── ws/ # WebSocket function
└── index.js

cloudbase-functions.json:

{
"functionsRoot": ".",
"functions": [
{
"name": "default",
"directory": "."
},
{
"name": "ws",
"directory": "ws",
"triggerPath": "/ws"
}
]
}

VSCode Editor Support

After installing the @cloudbase/functions-framework dependency, you can get schema auto-completion for cloudbase-functions.json in VSCode.

Create a .vscode/settings.json file in the project root directory:

{
"json.schemas": [
{
"fileMatch": ["cloudbase-functions.json"],
"url": "./node_modules/@cloudbase/functions-framework/functions-schema.json"
}
]
}

After configuration, field and type suggestions will automatically appear when editing the cloudbase-functions.json file.

Complete Example

Project Structure

my-api-service/
├── .vscode/
│ └── settings.json # VSCode configuration
├── package.json # Project configuration
├── scf_bootstrap # Bootstrap script
├── cloudbase-functions.json # Function routing configuration
├── index.js # Default function
├── user/ # User API
│ └── index.js
├── order/ # Order API
│ └── index.js
├── product/ # Product API
│ └── index.js
└── sse/ # SSE streaming response
└── index.js

cloudbase-functions.json

{
"functionsRoot": ".",
"functions": [
{
"name": "default",
"directory": ".",
"triggerPath": "/"
},
{
"name": "user",
"directory": "user",
"triggerPath": "/api/user"
},
{
"name": "order",
"directory": "order",
"triggerPath": "/api/order"
},
{
"name": "product",
"directory": "product",
"triggerPath": "/api/product"
},
{
"name": "sse",
"directory": "sse",
"triggerPath": "/stream"
}
]
}

FAQ

1. Route Configuration Not Taking Effect

Problem: Configured routing rules but still routing to the wrong function when accessing.

Solutions:

  • Check if the cloudbase-functions.json file is in the correct location
  • Check if the --functionsConfigFile parameter is specified in the scf_bootstrap startup script
  • Check the order of routing rules to ensure longest match rules are first
  • Use console.log to output context.httpContext.url to view the actual request path

2. Function Not Found

Problem: Startup error: Function xxx not found.

Solutions:

  • Check if the functions[].directory path is correct
  • Check if the functions[].source file exists
  • Check if the function name exported by functions[].target is correct
  • Ensure functionsRoot is set correctly

3. Route Conflict

Problem: Startup error: Route conflict.

Solutions:

  • Check if multiple route rules point to the same path
  • Check if there are duplicate configurations in triggerPath and routes
  • Ensure each path corresponds to only one function

4. Default Function Not Working

Problem: Configured default route with path: "/" but getting 404 when accessing.

Solutions:

  • Ensure the default route configuration is at the end of the routes array
  • Check if other route rules override the default route
  • Ensure the default function's directory and source configurations are correct

5. Hot Reload Not Working in Local Development

Problem: After modifying function code, need to restart the service for changes to take effect.

Solutions:

The @cloudbase/functions-framework framework does not currently support hot reload, requiring manual service restart:

# Stop service (Ctrl+C)
# Restart
./scf_bootstrap