Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions config/.claude/skills/cloud-functions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,23 @@ Use this skill when developing, deploying, and operating CloudBase cloud functio

- If the request is for SDK calls, timers, or event-driven workflows, write an **Event Function** with `exports.main = async (event, context) => {}`.
- If the request is for REST APIs, browser-facing endpoints, SSE, or WebSocket, write an **HTTP Function** with `req` / `res` on port `9000`.
- For Node.js HTTP Functions, default to the native `http` module unless the user explicitly asks for Express, Koa, NestJS, or another framework.
- If the user mentions HTTP access for an existing Event Function, keep the Event Function code shape and add gateway access separately.

## HTTP Function authoring contract

Use these rules whenever you are writing the function code itself:

- Do not write an HTTP Function as `exports.main(event, context)`. That is the Event Function contract.
- Treat the function as a standard web server process that must listen on port `9000`.
- With Node.js, prefer `http.createServer((req, res) => { ... })` by default so the runtime contract stays explicit.
- For Node.js HTTP Functions, choose one module system up front and keep it consistent. Default to CommonJS for simple functions (`require(...)`, no `"type": "module"` in `package.json`) unless you explicitly want ES Modules.
- If you do choose ES Modules (`"type": "module"` + `import ...`), do not mix in CommonJS-only globals or APIs such as `require(...)`, `module.exports`, or bare `__dirname`. In ESM, derive file paths from `import.meta.url` with `fileURLToPath(...)` only when needed.
- With the native `http` module, parse `req.url` yourself with `new URL(...)`, and read the request body from the stream before calling `JSON.parse`.
- Return responses explicitly with `res.writeHead(...)` and `res.end(...)`, including `Content-Type` such as `application/json; charset=utf-8` for JSON APIs.
- Keep routing and method handling explicit. Unknown paths should return `404`, and known paths with unsupported methods should normally return `405`.
- Keep gateway setup and security-rule changes separate from the runtime code. They affect access, not the HTTP Function programming model.

## Quick decision table

| Question | Choose |
Expand Down
162 changes: 134 additions & 28 deletions config/.claude/skills/cloud-functions/references/http-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ HTTP Functions are standard web services, not `exports.main(event, context)` han
- Listen on port `9000`.
- Ship an executable `scf_bootstrap` file.
- Include runtime dependencies in the package; HTTP Functions do not auto-install `node_modules` for you.
- For simple HTTP APIs, prefer the Node.js native `http` module so the function shape stays explicit and dependency-light. Only introduce Express, Koa, NestJS, or similar frameworks when the user explicitly asks for one or the service complexity justifies it.

## Minimal structure

Expand All @@ -25,7 +26,7 @@ my-http-function/

```bash
#!/bin/bash
node index.js
/var/lang/node18/bin/node index.js
```

Requirements:
Expand All @@ -37,54 +38,159 @@ Requirements:
## Minimal Node.js example

```javascript
const express = require("express");
const app = express();
const http = require("http");
const { URL } = require("url");

function sendJson(res, statusCode, data) {
res.writeHead(statusCode, { "Content-Type": "application/json; charset=utf-8" });
res.end(JSON.stringify(data));
}

function readJsonBody(req) {
return new Promise((resolve, reject) => {
let raw = "";

req.on("data", (chunk) => {
raw += chunk;
});

req.on("end", () => {
if (!raw) {
resolve({});
return;
}

try {
resolve(JSON.parse(raw));
} catch (error) {
reject(new Error("Invalid JSON body"));
}
});

req.on("error", reject);
});
}

const server = http.createServer(async (req, res) => {
const url = new URL(req.url || "/", "http://127.0.0.1");

if (req.method === "GET" && url.pathname === "/health") {
sendJson(res, 200, { ok: true });
return;
}

app.use(express.json());
if (req.method === "POST" && url.pathname === "/echo") {
try {
const body = await readJsonBody(req);
sendJson(res, 200, { received: body });
} catch (error) {
sendJson(res, 400, { error: error.message });
}
return;
}

app.get("/health", (req, res) => {
res.json({ ok: true });
sendJson(res, 404, { error: "Not Found" });
});

app.listen(9000);
server.listen(9000);
```

## Code-writing rules

- Do not write HTTP Functions as `exports.main = async (event, context) => {}`. That is the Event Function contract.
- Start an HTTP server explicitly with `http.createServer(...)` or a framework app, and always bind to port `9000`.
- Choose one Node.js module system and keep it consistent. For simple HTTP Functions, CommonJS is the safest default: use `require(...)` and leave `"type": "module"` out of `package.json`.
- If you intentionally use ES Modules, use `import ...` consistently and do not rely on CommonJS-only globals such as bare `__dirname`, `require(...)`, or `module.exports`. When you need the current file path in ESM, derive it from `import.meta.url`.
- Treat routing, method checks, and body parsing as part of the function code. With the native `http` module, parse `req.url` yourself and read the request body from the stream before calling `JSON.parse`.
- Return JSON responses explicitly and set `Content-Type` yourself, for example `application/json; charset=utf-8`.
- Keep unsupported routes and methods explicit. Return `404` for unknown paths, and return `405` when the path exists but the HTTP method is not allowed.
- Keep `scf_bootstrap`, `index.js`, `package.json`, and any bundled dependencies in the function directory that will be uploaded.

### Module system note

The minimal examples in this document use CommonJS:

- `const http = require("http")`
- no `"type": "module"` in `package.json`

That combination avoids the common ESM pitfall where `__dirname` is not defined. If you switch to ES Modules, switch the whole function to `import` syntax and update any file-path logic accordingly.

## Request handling rules

- `req.query` -> query string values.
- `req.body` -> parsed request body, but only after body-parsing middleware is configured.
- With Node native `http`, use `new URL(req.url, "http://127.0.0.1")` and read `url.searchParams` for query values.
- With Node native `http`, `req.body` does not exist. Read the body stream manually, then parse JSON yourself.
- `req.headers` -> incoming HTTP headers.
- `req.params` -> path parameters.
- Always send a response with `res.json()`, `res.send()`, or `res.status(...).json()`.
- Path parameters are framework-level conveniences. With the native `http` module, match `url.pathname` yourself.
- Always send a response explicitly. With Node native `http`, use `res.writeHead(...)` and `res.end(...)`.
- Return meaningful status codes such as `400`, `401`, `404`, `405`, `500`.

### Example with method checks

```javascript
const express = require("express");
const app = express();

app.use(express.json());

app.post("/users", (req, res) => {
const { name, email } = req.body;

if (!name || !email) {
return res.status(400).json({ error: "name and email are required" });
const http = require("http");
const { URL } = require("url");

function sendJson(res, statusCode, data) {
res.writeHead(statusCode, { "Content-Type": "application/json; charset=utf-8" });
res.end(JSON.stringify(data));
}

function readJsonBody(req) {
return new Promise((resolve, reject) => {
let raw = "";

req.on("data", (chunk) => {
raw += chunk;
});

req.on("end", () => {
if (!raw) {
resolve({});
return;
}

try {
resolve(JSON.parse(raw));
} catch (error) {
reject(new Error("Invalid JSON body"));
}
});

req.on("error", reject);
});
}

const server = http.createServer(async (req, res) => {
const url = new URL(req.url || "/", "http://127.0.0.1");

if (url.pathname === "/users" && req.method === "POST") {
try {
const { name, email } = await readJsonBody(req);

if (!name || !email) {
sendJson(res, 400, { error: "name and email are required" });
return;
}

sendJson(res, 201, { name, email });
} catch (error) {
sendJson(res, 400, { error: error.message });
}

return;
}

return res.status(201).json({ name, email });
});
if (url.pathname === "/users") {
sendJson(res, 405, { error: "Method Not Allowed" });
return;
}

app.all("/{*splat}", (req, res) => {
res.status(405).json({ error: "Method Not Allowed" });
sendJson(res, 404, { error: "Not Found" });
});

app.listen(9000);
server.listen(9000);
```

Express 5 note: do not use bare `*` or `/*` here. Express 5 uses `path-to-regexp` with named wildcards, so `app.all("/{*splat}", ...)` is the safe catch-all form when you also need to match the root path `/`.

## Deployment flow

Prefer `manageFunctions` over CLI in agent flows.
Expand Down
7 changes: 4 additions & 3 deletions config/source/editor-config/guides/cloudbase-rules.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -466,9 +466,10 @@ When users request deployment to CloudBase:
- Determine if this is a new deployment or update to existing services

1. **Backend Deployment (if applicable)**:
- Only for nodejs cloud functions: deploy directly using `manageFunctions(action="createFunction")` / `manageFunctions(action="updateFunctionCode")`
- Legacy compatibility: if older materials mention `createFunction`, `updateFunctionCode`, or `getFunctionList`, map them to the converged tools first
- Criteria: function directory contains `index.js` with cloud function format export: `exports.main = async (event, context) => {}`
- Only for Node.js cloud functions: deploy directly using `manageFunctions(action="createFunction")` / `manageFunctions(action="updateFunctionCode")`
- Legacy compatibility: if older materials mention `createFunction`, `updateFunctionCode`, or `getFunctionList`, map them to the converged tools first
- Before deploying, decide whether the function is Event or HTTP. Event Functions use `exports.main = async (event, context) => {}`.
- HTTP Functions are standard web services: they must listen on port `9000`, include `scf_bootstrap`, and for Node.js should default to native `http.createServer((req, res) => { ... })`. Parse `req.url` and the streamed request body manually, set response headers explicitly, and do not write the function as `exports.main` unless you intentionally choose Functions Framework.
- For other languages backend server (Java, Go, PHP, Python, Node.js): deploy to Cloud Run
- Ensure backend code supports CORS by default
- Prepare Dockerfile for containerized deployment
Expand Down
5 changes: 3 additions & 2 deletions config/source/guideline/cloudbase/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -368,9 +368,10 @@ When users request deployment to CloudBase:
- Determine if this is a new deployment or update to existing services

1. **Backend Deployment (if applicable)**:
- Only for nodejs cloud functions: deploy directly using `manageFunctions(action="createFunction")` / `manageFunctions(action="updateFunctionCode")`
- Only for Node.js cloud functions: deploy directly using `manageFunctions(action="createFunction")` / `manageFunctions(action="updateFunctionCode")`
- Legacy compatibility: if older materials mention `createFunction`, `updateFunctionCode`, or `getFunctionList`, map them to `manageFunctions(...)` and `queryFunctions(...)`
- Criteria: function directory contains `index.js` with cloud function format export: `exports.main = async (event, context) => {}`
- Before deploying, decide whether the function is Event or HTTP. Event Functions use `exports.main = async (event, context) => {}`.
- HTTP Functions are standard web services: they must listen on port `9000`, include `scf_bootstrap`, and for Node.js should default to native `http.createServer((req, res) => { ... })`. Parse `req.url` and the streamed request body manually, set response headers explicitly, and do not write the function as `exports.main` unless you intentionally choose Functions Framework.
- **Alternative: CLI Deployment** — If MCP is unavailable or the user prefers CLI, read the `cloudbase-cli` skill for `tcb`-based deployment workflows (functions, CloudRun, hosting).
- For other languages backend server (Java, Go, PHP, Python, Node.js): deploy to Cloud Run
- Ensure backend code supports CORS by default
Expand Down
3 changes: 2 additions & 1 deletion config/source/guideline/cloudbase/activation-map.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,8 @@ scenarios:
- auth-web
commonMistakes:
- 把 Web 登录逻辑错误地放进云函数。
- HTTP 函数遗漏 `scf_bootstrap` 或 9000 端口要求。
- 把 HTTP 函数误写成 `exports.main(event, context)`,或误以为 Node 原生 `http` 请求里自带 `req.body`。
- HTTP 函数遗漏 `scf_bootstrap`、9000 端口或显式响应头。

- id: cloudrun-backend
priority: 85
Expand Down
15 changes: 15 additions & 0 deletions config/source/skills/cloud-functions/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,23 @@ Use this skill when developing, deploying, and operating CloudBase cloud functio

- If the request is for SDK calls, timers, or event-driven workflows, write an **Event Function** with `exports.main = async (event, context) => {}`.
- If the request is for REST APIs, browser-facing endpoints, SSE, or WebSocket, write an **HTTP Function** with `req` / `res` on port `9000`.
- For Node.js HTTP Functions, default to the native `http` module unless the user explicitly asks for Express, Koa, NestJS, or another framework.
- If the user mentions HTTP access for an existing Event Function, keep the Event Function code shape and add gateway access separately.

## HTTP Function authoring contract

Use these rules whenever you are writing the function code itself:

- Do not write an HTTP Function as `exports.main(event, context)`. That is the Event Function contract.
- Treat the function as a standard web server process that must listen on port `9000`.
- With Node.js, prefer `http.createServer((req, res) => { ... })` by default so the runtime contract stays explicit.
- For Node.js HTTP Functions, choose one module system up front and keep it consistent. Default to CommonJS for simple functions (`require(...)`, no `"type": "module"` in `package.json`) unless you explicitly want ES Modules.
- If you do choose ES Modules (`"type": "module"` + `import ...`), do not mix in CommonJS-only globals or APIs such as `require(...)`, `module.exports`, or bare `__dirname`. In ESM, derive file paths from `import.meta.url` with `fileURLToPath(...)` only when needed.
- With the native `http` module, parse `req.url` yourself with `new URL(...)`, and read the request body from the stream before calling `JSON.parse`.
- Return responses explicitly with `res.writeHead(...)` and `res.end(...)`, including `Content-Type` such as `application/json; charset=utf-8` for JSON APIs.
- Keep routing and method handling explicit. Unknown paths should return `404`, and known paths with unsupported methods should normally return `405`.
- Keep gateway setup and security-rule changes separate from the runtime code. They affect access, not the HTTP Function programming model.

## Quick decision table

| Question | Choose |
Expand Down
Loading
Loading