EdgeOne Makers Cloud Functions Node.js
Node.js 是 Cloud Functions 最常用的运行时之一,语法和前端一脉相承,上手快,npm 生态又极其丰富,大部分场景都有现成的包可以用。这一篇从头到尾走一遍 Node.js 函数的开发流程,包括项目结构、依赖管理、完整示例代码。
项目结构
一个典型的 Node.js Cloud Function 项目长这样。
my-function/
├── index.js # 入口文件
├── package.json # 依赖声明
├── .env # 本地环境变量(不上传)
└── lib/ # 其他模块
└── utils.js
index.js 是入口文件,函数从这里开始执行。package.json 声明依赖,部署时平台会根据它执行 npm install。
入口函数
Cloud Functions 的入口函数固定叫 main,接收 event 和 context 两个参数。
// index.js
exports.main = async function(event, context) {
// event: 触发信息,HTTP 触发时包含请求数据
// context: 运行上下文,包含函数元信息和请求 ID
console.log('函数被触发了', JSON.stringify(event));
return {
statusCode: 200,
headers: {
'content-type': 'application/json; charset=utf-8'
},
body: JSON.stringify({
message: 'Hello Node.js Cloud Function'
})
};
};
返回值是一个对象,平台会把它转成 HTTP 响应。
| 字段 | 类型 | 说明 |
|---|---|---|
| statusCode | Number | HTTP 状态码 |
| headers | Object | 响应头 |
| body | String | 响应体,必须是字符串 |
| isBase64Encoded | Boolean | body 是否经过 base64 编码 |
处理 HTTP 请求
HTTP 触发时,event 对象里包含请求的全部信息。
读取请求信息
// index.js
exports.main = async function(event, context) {
// HTTP 触发时,event 里的关键字段
const {
httpMethod, // GET、POST 等
path, // 请求路径
headers, // 请求头
queryStringParameters, // 查询参数
body // 请求体(字符串)
} = event;
// 读取查询参数
const keyword = queryStringParameters?.keyword || '没有传关键词';
// 读取请求头
const contentType = headers['content-type'] || '未知';
// 返回信息给客户端
return {
statusCode: 200,
headers: { 'content-type': 'application/json; charset=utf-8' },
body: JSON.stringify({
method: httpMethod,
path: path,
keyword: keyword,
contentType: contentType
})
};
};
处理 POST 请求的 JSON 数据
// index.js
exports.main = async function(event, context) {
// POST 请求的 body 是字符串,需要手动解析
if (event.httpMethod === 'POST') {
let data;
try {
data = JSON.parse(event.body || '{}');
} catch (e) {
return {
statusCode: 400,
body: JSON.stringify({ error: '请求体 JSON 格式不对' })
};
}
// 拿到解析后的数据
const name = data.name || '匿名';
const age = data.age || 0;
return {
statusCode: 200,
headers: { 'content-type': 'application/json; charset=utf-8' },
body: JSON.stringify({
message: `收到数据, 名字: ${name}, 年龄: ${age}`
})
};
}
return {
statusCode: 405,
body: JSON.stringify({ error: '只接受 POST 请求' })
};
};
路径参数
如果路由配置了路径参数,比如 /api/users/{id},event.pathParameters 里能拿到。
// index.js
exports.main = async function(event, context) {
// 路径参数在 pathParameters 里
const userId = event.pathParameters?.id;
if (!userId) {
return {
statusCode: 400,
body: JSON.stringify({ error: '缺少用户 ID' })
};
}
return {
statusCode: 200,
headers: { 'content-type': 'application/json; charset=utf-8' },
body: JSON.stringify({
userId: userId,
name: '用户' + userId
})
};
};
依赖管理
使用 package.json 声明依赖
{
"name": "my-cloud-function",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"axios": "^1.6.0",
"dayjs": "^1.11.0"
}
}
部署时平台会自动执行 npm install,把依赖装好。你也可以本地先 npm install 测试通过后再部署。
使用内置模块
Node.js 自带的模块不需要安装,直接 require 就行。
// index.js
const crypto = require('crypto');
const querystring = require('querystring');
exports.main = async function(event, context) {
// 用 crypto 生成随机字符串
const token = crypto.randomBytes(16).toString('hex');
return {
statusCode: 200,
headers: { 'content-type': 'application/json; charset=utf-8' },
body: JSON.stringify({ token: token })
};
};
环境变量
在控制台配置的环境变量通过 process.env 读取。
// index.js
exports.main = async function(event, context) {
// 读取环境变量
const apiKey = process.env.API_KEY;
const dbHost = process.env.DB_HOST;
if (!apiKey) {
return {
statusCode: 500,
body: JSON.stringify({ error: '缺少 API_KEY 环境变量' })
};
}
return {
statusCode: 200,
headers: { 'content-type': 'application/json; charset=utf-8' },
body: JSON.stringify({
dbHost: dbHost || '未配置',
apiKeySet: true
})
};
};
完整示例: 待办事项 API
下面是一个完整的待办事项接口,演示增删查的常见操作。数据存在内存里(实际项目应该用数据库),但结构和写法可以直接拿去用。
// index.js
// 内存中的待办列表(演示用,实际应该用数据库)
let todos = [];
let nextId = 1;
exports.main = async function(event, context) {
const method = event.httpMethod;
const path = event.path;
// 简单的路由分发
if (path === '/api/todos' && method === 'GET') {
// 查询全部
return {
statusCode: 200,
headers: { 'content-type': 'application/json; charset=utf-8' },
body: JSON.stringify({ data: todos })
};
}
if (path === '/api/todos' && method === 'POST') {
// 新增一条
const data = JSON.parse(event.body || '{}');
if (!data.title) {
return {
statusCode: 400,
body: JSON.stringify({ error: 'title 不能为空' })
};
}
const newTodo = {
id: nextId++,
title: data.title,
done: false,
createdAt: new Date().toISOString()
};
todos.push(newTodo);
return {
statusCode: 201,
headers: { 'content-type': 'application/json; charset=utf-8' },
body: JSON.stringify({ data: newTodo })
};
}
if (path.startsWith('/api/todos/') && method === 'DELETE') {
// 删除一条
const id = parseInt(event.pathParameters?.id, 10);
const index = todos.findIndex(t => t.id === id);
if (index === -1) {
return {
statusCode: 404,
body: JSON.stringify({ error: '找不到这条待办' })
};
}
todos.splice(index, 1);
return {
statusCode: 200,
headers: { 'content-type': 'application/json; charset=utf-8' },
body: JSON.stringify({ message: '删除成功' })
};
}
return {
statusCode: 404,
body: JSON.stringify({ error: '路由不存在' })
};
};
对应的路由配置。
| 路径 | 方法 | 功能 |
|---|---|---|
| /api/todos | GET | 查询全部待办 |
| /api/todos | POST | 新增一条待办 |
| /api/todos/{id} | DELETE | 删除指定待办 |
速查卡片
| 要点 | 说明 |
|---|---|
| 入口函数 | exports.main = async function(event, context) |
| HTTP 请求信息 | event.httpMethod, event.path, event.headers, event.body |
| 查询参数 | event.queryStringParameters |
| 路径参数 | event.pathParameters |
| 返回值格式 | { statusCode, headers, body },body 必须是字符串 |
| 依赖管理 | package.json 声明,部署时自动 npm install |
| 环境变量 | process.env.KEY 读取 |
| 调试方式 | console.log 输出,控制台日志模块查看 |