mcpField
N
netlify-functions

netlify-functions

github.com/netlify开发473.3k 次安装6 天前更新
代码仓库

关于 netlify-functions

netlify-functions 是为 Netlify 项目编写 Functions 的技能,面向开发者,涵盖现代 Request/Response 处理器、路由配置及后台、定时和平台事件任务。

主要功能

  1. 01编写默认异步处理器
  2. 02配置自定义路径与方法
  3. 03读取请求上下文和环境变量
  4. 04设置后台与定时任务
  5. 05处理平台事件
  6. 06配置流式响应与资源

适用场景

  1. 01创建 HTTP 接口处理请求
  2. 02安排定时或后台工作
  3. 03响应部署、身份或表单事件

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/netlify/context-and-tools --skill netlify-functions

安装后在 Claude Code 中输入/netlify-functions确认已加载。

详细介绍

Netlify Functions

使用现代默认处理程序 API(.mts TypeScript)。导出一个异步默认处理程序,接收 Web Request 和 Netlify Context,并返回 Web Response。除非使用 Go 或迁移旧代码(参见末尾的 Legacy),否则请避免使用旧版 AWS Lambda 处理程序形式。

文件位置

  • 默认目录:netlify/functions/(相对于基础目录)。请将其置于发布目录之外,否则源文件会作为静态资源发布。
  • 函数可以是单个文件,也可以是一个子目录,其入口文件名为 index 或与子目录同名。以下写法都会创建函数 hello:
    • netlify/functions/hello.mts
    • netlify/functions/hello/hello.mts
    • netlify/functions/hello/index.mts
  • ES 模块使用 .mts(TS)/ .mjs(JS)。.cts/.cjs 会强制使用 CommonJS;.ts/.js 则遵循最近的 package.json 中的 "type"。

⚠️ 同名的 JS 和 TS 文件:JS 文件优先。 如果函数目录中同时有 items.js 和 items.ts,部署的是 JavaScript 函数,而 TypeScript 文件会被静默忽略——修改 .ts 不会产生任何效果。每个函数名称只保留一个文件;将函数迁移到 TypeScript 时,请删除或重命名过时的 .js 文件。

最简函数

无需导出 config。访问路径为 /.netlify/functions/hello。

import type { Context } from "@netlify/functions"

export default async (req: Request, context: Context) => {
  return new Response("Hello, world!")
}

安装类型定义:npm install @netlify/functions(TS 类型必需;JS 可选)。

使用 Netlify.env.get() 读取环境变量和密钥:

const apiKey = Netlify.env.get("STRIPE_SECRET_KEY")

切勿将密钥硬编码。要让变量在运行时可用,其作用范围必须包含 Functions。在 netlify.toml 中设置的变量不会提供给函数。变量值会在每次部署时冻结——更改后需重新部署才能生效。

在代码中设置响应标头,并在返回的 Response 上设置。netlify.toml 中的 [[headers]]、_headers 和重定向标头规则仅适用于静态 CDN 响应,不适用于函数响应。除非明确要求,否则不要添加 CORS 标头。

自定义路径路由

设置 config.path 可将请求路由到自定义 URL。设置后,函数仅在该路径提供服务,不会在 /.netlify/functions/<name> 提供服务。

import type { Config, Context } from "@netlify/functions"

export default async (req: Request, context: Context) => {
  const { city, country } = context.params
  return new Response(`You're visiting ${city} in ${country}!`)
}

export const config: Config = {
  path: "/travel-guide/:city/:country",
}
  • 多个路径:path: ["/cats", "/dogs"]。
  • 模式:path 支持 URLPattern 语法,例如 path: ["/sale/*", "/item/:sku"]。命名组会放在 context.params 中。查询字符串请使用 req.url。
  • excludedPath:排除特定路径,例如使用 path: "/product/*" 时设置 excludedPath: ["/product/*.css"]。
  • preferStatic: true:优先匹配 URL 对应的真实静态文件。
  • method:限制请求方法,例如 method: ["GET", "POST"]。

可获取模块形式(替代方案)

与基础处理程序等效;将 config 内联其中,并允许添加事件处理程序。

import type { NetlifyFunction } from "@netlify/functions"

export default {
  fetch: (req, context) => new Response("Hello, world!"),
  config: { path: "/hello" },
} satisfies NetlifyFunction

Context 对象

处理程序的第二个参数(或在处理程序作用域之外使用 @netlify/functions 中的 getContext()——在请求之外调用会抛出异常;请用 try/catch 包装)。

  • context.params — 命名路径参数。
  • context.geo — city、country.code/name、latitude、longitude、subdivision、timezone、postalCode。
  • context.ip — 客户端 IP 字符串。
  • context.cookies — get(name) / set(options) / delete(name|options)。跨子域 Cookie 需要自定义域名(netlify.app 位于 Public Suffix List 中)。
  • context.site — id、name、url。context.deploy — context、id、published、skewProtectionToken。context.account.id。context.server.region。context.requestId。
  • context.user — 登录了私有或受团队登录保护项目的 Netlify 用户:id、email(可能是 undefined)、expiresAt(Date)。详见下文。
  • context.waitUntil(promise) — 在响应发送后运行任务(分析、日志),且不会阻塞响应。计费/日志时长会持续计算,直到 promise 完成。适用于 2025-03-20 及之后部署的函数。

⚠️ 在 netlify dev 下,context.geo/context.ip 是模拟值——请使用 netlify dev --geo=mock --country=DE 进行测试。

调用方:context.user

⚠️ context.user 通常为 undefined:公开项目、基础密码保护、本地 netlify dev,以及 2026 年 10 月 5 日之前部署的函数(重新部署即可启用)。务必妥善处理;使用 id 作为记录键(email 可能为 undefined)。

import type { Config, Context } from "@netlify/functions"

export default async (req: Request, context: Context) => {
  if (!context.user) {
    // Public project, basic password protection, local CLI, or pre-2026-10-05 deploy.
    return Response.json({ user: null }, { status: 401 })
  }
  return Response.json({
    id: context.user.id,
    email: context.user.email ?? null,
    expiresAt: context.user.expiresAt.toISOString(),
  })
}

export const config: Config = { path: "/whoami" }

配置对象

导出 const config(或 Fetchable 模块的 config 属性):

  • path / excludedPath — string | string[],必须以 / 开头。
  • method — 单个方法或数组。
  • preferStatic — boolean。
  • background — boolean(见后台函数)。
  • schedule — cron 字符串(见定时函数)。不能与 path / excludedPath 同时使用。
  • rateLimit — { action: 'rate_limit'|'rewrite', aggregateBy: 'domain'|'ip'|[...], to?, windowSize, windowLimit }。
  • memory / vcpu — 见下文;不能同时使用。
  • region — 机场代码;见下文。

集成

import type { Config } from "@netlify/functions"
import { getDatabase } from "@netlify/database"

const db = getDatabase()

export default async (req: Request) => {
  const users = await db.sql`SELECT id, email FROM users LIMIT 10`
  return Response.json({ users })
}

export const config: Config = { path: "/users" }

Blobs:import { getStore } from "@netlify/blobs";getStore("uploads").set(key, await req.blob())。

缓存:来自 @netlify/functions 的 await purgeCache({ tags: ["products"] }) 会使边缘缓存失效(省略 tags 可清除所有缓存)。

流式响应

将 ReadableStream 作为 Response 正文返回。限制:执行时间 60s,响应大小 20 MB。

export default async (req: Request) => {
  const res = await fetch("https://api.openai.com/v1/chat/completions", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Authorization: `Bearer ${Netlify.env.get("OPENAI_API_KEY")}`,
    },
    body: JSON.stringify({ model: "gpt-4o-mini", stream: true, messages: [/* ... */] }),
  })
  return new Response(res.body, { headers: { "content-type": "text/event-stream" } })
}

后台函数(长时间运行)

设置 config.background: true。客户端会立即收到 202;返回值会被丢弃;最长运行 15 分钟。不支持流式传输。重试:调用出错后,1 分钟后重试,再过 2 分钟重试一次。请将结果发送到客户端以外的位置。

import type { Config } from "@netlify/functions"

export default async (req: Request) => {
  // Long-running work. Client already has its 202.
}

export const config: Config = { background: true, path: "/process" }

限制:后台负载上限为 256 KB。旧版 -background 文件名后缀仍然有效,但建议使用 config.background。

定时函数(cron)

使用 cron 表达式设置 config.schedule,并以 UTC 执行。请求正文为包含 next_run(ISO-8601)的 JSON。内联配置仅支持 TS/JS — Go 必须使用 netlify.toml。

请始终计算目标本地时间对应的 UTC 时间。例如,ET 上午 9 点 → UTC "0 13 * * *"(注意,夏令时会使其相差一小时;请选择所需的 UTC 时差)。建议使用明确的 cron 表达式,而不要使用 @daily / @hourly 快捷方式,因为它们无法指定特定的本地小时。

import type { Config } from "@netlify/functions"

export default async (req: Request) => {
  const { next_run } = await req.json()
  console.log("Next invocation at:", next_run)
}

export const config: Config = {
  schedule: "0 13 * * *", // 9 AM ET (EST); UTC
}

通过 netlify.toml 配置(支持所有语言):

[functions."daily-digest"]
  schedule = "0 13 * * *"

限制:30s 时限(运行时间更长时请使用后台函数);仅在已发布的部署上触发(不会在 Deploy Previews / 分支部署上触发 — 请使用 Run now 手动调用);无法通过 URL 调用;不支持流式传输;不支持请求负载 / POST 数据;与 Split Testing 不兼容。支持所有扩展,但不包括 @reboot 和 @annually。

平台事件函数

导出一个默认对象,其中的处理程序以事件命名。它们始终在后台运行 — 不会向客户端返回响应。可与同一函数中的 fetch 结合使用。每个处理程序都有完整类型;从 @netlify/functions 导入事件类型。

import type { DeploySucceededEvent, DeployFailedEvent } from "@netlify/functions"

export default {
  deploySucceeded(event: DeploySucceededEvent) {
    console.log(`Deploy ${event.deploy.id} succeeded for ${event.site.name}`)
  },
  deployFailed(event: DeployFailedEvent) {
    console.log(`Deploy ${event.deploy.id} failed: ${event.deploy.errorMessage}`)
  },
}

部署事件(event.deploy、event.site;返回 void):deployBuilding、deploySucceeded、deployFailed、deployDeleted、deployLocked、deployUnlocked。

Identity 事件(event.user,仅保证有 id):

处理程序能否拒绝?能否修改?
userValidate是是
userSignup是是
userLogin是是
userModified是是
userDeleted否否
  • 拒绝:在处理程序中调用 event.deny() → 最终用户会收到 401。第一个拒绝的函数会中止整个调用链。
  • 修改:返回 { user: {...} } 以保存更改;返回 undefined 则继续传递。

表单事件:formSubmitted → event.data(以字段名为键的对象)。返回 void。

多个函数可以处理同一事件(都会运行)。Netlify 会为每个事件签名(JWS),并在调用前进行验证,以阻止外部请求。旧版文件名约定(文件名与事件同名,通过 await req.json() 获取负载 → payload)仍然有效,但建议使用带类型的处理程序。

区域

⚠️ 除非有明确理由,否则不要覆盖 config.region;默认值 cmh(美国东部,俄亥俄州)是有意设置的。

有充分理由时——例如,数据库位于欧盟:

export const config: Config = { path: "/eu-data", region: "dub" }

机场代码(自助选择):cmh、dub、fra、gru、iad、lhr、nrt、pdx、sfo、sin、syd、yul。需支持人员协助:cdg、mxp。每个函数只在一个区域运行(不支持多区域地理路由)。选择区域需要 Pro/Enterprise。框架适配器生成的函数无法使用 export const config——请在 UI 中通过 Cloud compute > Functions > Region 设置项目级区域。更改区域后,重新部署。函数级区域优先于站点级 UI 设置。

内存 / vCPU

⚠️ 不要猜测性地设置 config.memory 或 config.vcpu——计费会随规格线性增长。

有充分理由时(例如,发现处理大型 PDF 时发生 OOM):

export const config: Config = { path: "/heavy", memory: "2gb" } // or memory: 2048
  • memory:1024–4096 MB。vcpu:0.5–2.0(0.5 → 1024 MB,2.0 → 4096 MB)。两者不能同时设置;Netlify 会自动确定另一项。需要基于额度计费的 Pro/Enterprise。通过 netlify.toml 设置:[functions.heavy]\n memory = "2gb"。

打包与磁盘上的文件

⚠️ 运行时通过 fs.readFile 读取的文件不会被打包(生产环境中会出现 ENOENT)。请将静态数据作为模块导入,或声明这些文件:

[functions]
  included_files = ["files/*.md"]
  external_node_modules = ["package-1"]

⚠️ 所有函数的环境变量合计上限为约 4 KB——不要将 PEM 密钥 / 服务账号 JSON 放入环境变量。

仅适用于 JS 的 esbuild:[functions]\n node_bundler = "esbuild"。

限制(不可配置)

  • 同步执行:60s。定时执行:30s。后台执行:15 min。
  • 缓冲请求 / 响应载荷:6 MB(二进制内容会进行 Base64 编码,额外开销约为 30% → 二进制内容的有效上限为 4.5 MB)。
  • 流式响应:20 MB。后台载荷:256 KB。

本地测试与部署

  • 大多数框架都能在开发服务器中模拟函数。Vite 框架(Astro、Nuxt、TanStack Start、React Router):安装 @netlify/vite-plugin 并运行开发服务器。Next.js 和其他框架:使用 Netlify CLI(netlify dev)。
  • 定时函数不会在本地按计划触发——请使用 netlify functions:invoke <name> 手动调用一次。
  • 部署:推送到 Git 以持续部署,或使用 Netlify CLI/API。
  • 日志和指标位于 Netlify UI 中;可通过 CLI 实时查看。所有已部署的函数版本都会显示在 Functions 标签页下;使用列表顶部的搜索框按名称筛选函数,并使用单独的筛选器选择分支或输入 Deploy Preview 编号。

Node 运行时版本

运行时版本与构建使用的 Node.js 版本一致(回退版本:Node.js 24)。如需覆盖,可通过 UI/CLI/API 设置环境变量 AWS_LAMBDA_JS_RUNTIME(例如 nodejs24.x)——不要在 netlify.toml 中设置——然后重新部署。ES 模块中无法使用 __dirname/__filename,请改用 import.meta.url;CommonJS 包的命名导入会失败,请使用默认导入。

旧版 / Go(非必要时避免使用)

Go 必须使用 兼容 Lambda 的 API;Go 的路由 / 区域 / 内存需在 netlify.toml 中设置。若要迁移采用 Lambda 风格的 JS/TS,@netlify/aws-lambda-compat 可封装 AWS 处理程序:

import { withLambda } from "@netlify/aws-lambda-compat"
import type { HandlerContext, HandlerEvent, HandlerResponse } from "@netlify/aws-lambda-compat"

export default withLambda(async (event: HandlerEvent, context: HandlerContext): Promise<HandlerResponse> => {
  const name = event.queryStringParameters?.name ?? "World"
  return { statusCode: 200, headers: { "content-type": "application/json" }, body: JSON.stringify({ name }) }
})

Netlify 规范(函数)

这些是组织约定,并非文档事实——它们由 ctx-gen 合并到渲染后的 skill 中,且不会自动生成。内容提取自之前手写的 netlify-functions skill; 由 skills 维护者负责维护。

  1. 尽可能使用 TypeScript(.mts)。
  2. 通过 Netlify.env.get() 访问环境变量(为保持一致性,优先使用它,而不是 process.env)。
  3. 除非明确要求,否则切勿添加 CORS 标头。
  4. 将密钥存储在环境变量中,切勿写入代码。
  5. 在 netlify dev 下,context.geo 和 context.ip 是模拟值——只是占位值,并非真实位置或客户端 IP。不要因为本地值始终不变就断定地理位置代码有问题;使用 netlify dev --geo=mock --country=DE 测试不同分支,并在部署后验证。
  6. 切勿凭空设置 config.memory 或 config.vcpu。只有在已知工作对内存或计算资源要求很高,或观察到函数自身工作导致的 OOM/超时时,才提高这些值——费用会随规格线性增长。
  7. 除非用户明确说明了具体原因(数据库/后端同区域部署、数据驻留或区域受众),否则切勿覆盖 config.region。默认值 cmh 是经过慎重选择的。
  8. 运行时从磁盘读取的文件(对模板、JSON、WASM 使用 fs.readFile)不会被打包:在 netlify dev 下可正常运行,在生产环境中则会出现 ENOENT。优先将静态数据作为模块导入;否则,在 netlify.toml 中使用限定范围的 included_files 条目声明该文件。
  9. 约 4 KB 的环境变量总大小限制适用于所有函数(它们都运行在 AWS Lambda 上),不只适用于 Lambda 兼容模式。不要将大型负载(服务账号 JSON、PEM 密钥)放入环境变量——请改用打包文件、Blobs 或运行时获取。没有任何 Netlify 设置可以提高此上限。
  10. 正文中的第一个函数示例必须是最简默认示例:完全不导出 config,并说明该函数位于 /.netlify/functions/<name>。自定义 path 路由只能出现在后续示例中——Agent 会模仿它们看到的第一个示例。
  11. 切勿在通用示例中演示 memory、vcpu 或 region——只有在明确说明原因(观察到 OOM、后端同区域部署、数据驻留)时才展示这些设置。
  12. 定时函数示例必须使用真实 cron 表达式,并明确说明 UTC 转换(例如,美国东部时间上午 9 点 → UTC 的 "0 13 * * *",并注明夏令时)——切勿只使用 @hourly/@daily 这类快捷写法,因为它们无法指定某个本地时刻。
  13. 正文必须说明:netlify.toml 中的 [[headers]]、_headers 和重定向标头规则仅适用于静态 CDN 响应——函数的响应标头需在代码中通过返回的 Response 设置。
  14. 当用户要求构建一个执行具体工作的函数(生成报告、处理上传、发送摘要)时,必须实现实际工作——需要时选择合适的真实库,并端到端实现操作。切勿将核心任务交付为置于已完成的基础架构之后的 not implemented 占位实现:如果函数的核心分支会抛出错误,那么无论配置和路由多完善,它都不是可用的答案。
  15. config.background: true 是文档说明且当前受支持的后台函数设置方式(文件名添加 -background 后缀也仍然有效)。如果本地安装的打包工具无法识别此标记,应首先考虑版本不匹配:检查并升级本地工具,并将本地兼容性问题与平台支持情况分开说明——切勿仅因安装的旧版本架构不支持,就从答案中移除文档推荐的标记。
  16. 对于私有项目或启用了团队登录保护的项目,context.user 表示发起请求的 Netlify 用户(id、email、expiresAt)。如果登录保护后的应用需要知道调用者身份(按个人区分的数据、审计日志、审批、个人设置),应读取 context.user,而不是再添加另一套登录。Netlify 会在函数运行前验证登录,因此切勿从客户端发送的标头或 Cookie 中获取身份信息。每个示例都必须处理 context.user 为 undefined 的情况:公开项目、基本密码保护、netlify dev,以及在 2026 年 10 月 5 日之前部署且之后尚未重新部署的函数。email 也可能是 undefined,因此存储的记录应以 id 为键。这是 Netlify 账号,不是 Netlify Identity 用户;对于 Identity,请使用 @netlify/identity 中的 getUser()。

评论

支持 Markdown