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/user → user function
  • /api/user/list → user function
  • /api/user/profile → userProfile function (longest match)
  • /api/user/profile/detail → userProfile 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/user → user function
  • /api/order → order function
  • / → default function
  • /about → default function
  • /health → default 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