netlify-functions

Write, configure, and deploy Netlify serverless functions in TypeScript, JavaScript, or Go. Use this when adding an API endpoint or backend route, adding a contact form handler, wiring auth or Identity signup/login hooks, building streaming or AI-proxy responses, scheduling cron jobs, running long b

By netlify · 2,369 installs

npx skills add netlify/context-and-tools --skill netlify-functions

Source repository · Upstream listing

Netlify Functions Reach for the modern default handler API ( .mts TypeScript). Export a default async handler taking a web Request and a Netlify Context , returning a web Response . Avoid the legacy AWS Lambda handler shape unless writing Go or migrating old code (see Legacy at the end). File locations Default directory: netlify/functions/ (relative to base directory). Keep it outside your publish directory or source files ship as static assets. A function is one file or a subdirectory whose entry file is named index or matches the subdirectory name. All of these create a function hello : netlify/functions/hello.mts netlify/functions/hello/hello.mts netlify/functions/hello/index.mts Use .mts (TS) / .mjs (JS) for ES modules. .cts / .cjs force CommonJS; .ts / .js follow the nearest package.json "type" . ⚠️ Same name JS and TS files: the JS one wins. If the functions directory has e.g. items.js and items.ts , the JavaScript function is deployed and the TypeScript file is silently ignored — edits to the .ts have no effect. Keep one file per function name; delete or rename the stale .js when moving a function to TypeScript. Minimal function No config export. Serves at /.netlify/functions/hello . Install types: npm install @netlify/functions (required for TS types; optional for JS). Read env vars and secrets with Netlify.env.get() : Never hardcode secrets. For the variable to exist at runtime its scope must include Functions . Variables set in netlify.toml are NOT available to functions. Values are frozen per deploy — change them and redeploy to apply. Response headers are set in code on the returned Response . [[headers]] in netlify.toml , headers , and redirect header rules apply ONLY to static CDN responses, not function responses. Do not add CORS headers unless explicitly requested. Custom path routing Set config.path to route to custom URLs. When set, the function serves ONLY at that path — not at /.netlify/functions/<name . Multiple paths: path: ["/cats", "/dogs"] . Patterns: path supports [ URLPattern ](https://developer.mozilla.org/en US/docs/Web/API/URL Pattern API) syntax — path: ["/sale/ ", "/item/:sku"] . Named groups land on context.params . For the query string use req.url . excludedPath : carve exceptions, e.g. excludedPath: ["/product/ .css"] with path: "/product/ " . preferStatic: true : let a real static file at the URL win. method : restrict methods, e.g. method: ["GET", "POST"] . Fetchable module shape (alternative) Equivalent to the bare handler; carries config inline and lets you add event handlers. Context object Second handler argument (or getContext() from @netlify/functions when out of handler scope — throws outside a request; wrap in try/catch). context.params — named path params. context.geo — city , country.code/name , latitude , longitude , subdivision , timezone , postalCode . context.ip — client IP string. context.cookies — get(name) / set(options) / delete(name options) . Cross subdomain cookies need a custom domain ( netlify.app is on the Public Suffix List). context.site — id , name , url . context.deploy — context , id , published , skewProtectionToken . context.account.id . context.server.region . context.requestId . context.user — the Netlify user who logged in to a private or team login protected project: id , email (may be undefined ), expiresAt ( Date ). See below. context.waitUntil(promise) — run work after the response is sent (analytics, logs) without blocking. Billing/log duration counts until the promise settles. Available for functions deployed on/after 2025 03 20. ⚠️ context.geo / context.ip are mocked under netlify dev — test with netlify dev geo=mock country=DE . Who is calling: context.user ⚠️ context.user is often undefined : public projects, basic password protection, local netlify dev , and functions deployed before October 5, 2026 (redeploy to enable). Always handle it; key records on id ( email may be undefined ). Config object Export const config (or the config property of a Fetchable module): path / excludedPath — string string[] , must start with / . method — one method or array. preferStatic — boolean . background — boolean (see Background). schedule — cron string (see Scheduled). Mutually exclusive with path / excludedPath . rateLimit — { action: 'rate limit' 'rewrite', aggregateBy: 'domain' 'ip' [...], to?, windowSize, windowLimit } . memory / vcpu — see below; mutually exclusive. region — airport code; see below. Integrations Blobs: import { getStore } from "@netlify/blobs" ; getStore("uploads").set(key, await req.blob()) . Cache: await purgeCache({ tags: ["products"] }) from @netlify/functions invalidates the edge cache (omit tags to purge all). Streaming responses Return a ReadableStream as the Response body. Limits: 60s execution, 20 MB response . Background functions (long running) config.background: true . Client gets an immediate 202 ; the return value is discarded; runs up to 15 minutes . No streaming. Retries: on invocation error, retry after 1 min, then again 2 min later. Send results somewhere other than the client. Limits: background payload 256 KB . Legacy background filename suffix still works but prefer config.background . Scheduled functions (cron) config.schedule with a cron expression, executed in UTC . The request body is JSON with next run (ISO 8601). Inline config is TS/JS only — Go must use netlify.toml . Always compute the UTC time for the target local hour. E.g. 9 AM ET → "0 13 " UTC (note this shifts by an hour across DST; pick the UTC offset you need). Prefer explicit cron over @daily / @hourly shortcuts, which can't target a specific local hour. Via netlify.toml (all languages): Constraints: 30s limit (use background for longer); only fire on published deploys (not Deploy Previews/branch deploys — invoke manually with Run now ); no URL invocation; no streaming; no request payloads/POST data; incompatible with Split Testing. All extensions supported except @reboot and @annually . Platform event functions Export a default object with handlers named after events. They always run in the background — no response to a client. Combine with fetch in the same function. Every handler is fully typed; import event types from @netlify/functions . Deploy events ( event.deploy , event.site ; return void ): deployBuilding , deploySucceeded , deployFailed , deployDeleted , deployLocked , deployUnlocked . Identity events ( event.user , only id guaranteed): Handler Can deny? Can mutate? userValidate Yes Yes userSignup Yes Yes userLogin Yes Yes userModified Yes Yes userDeleted No No Deny: call event.deny() inside the handler → end user gets 401 . First function to deny aborts the chain. Mutate: return { user: {...} } to persist changes; return undefined to pass through. Form events : formSubmitted → event.data (object keyed by field name). Return void . Multiple functions can handle the same event (all run). Netlify signs each event (JWS) and verifies before invoking, blocking external requests. Legacy filename convention (file named after the event, payload via await req.json() → payload ) still works but prefer typed handlers. Region ⚠️ Do NOT override config.region without a stated reason; the cmh (US East, Ohio) default is deliberate. When justified — e.g. an EU resident database: Airport codes (self serve): cmh , dub , fra , gru , iad , lhr , nrt , pdx , sfo , sin , syd , yul . Support assisted: cdg , mxp . Each function runs in exactly one region (no multi region geo routing). Region selection needs Pro/Enterprise. Framework adapter generated functions can't take export const config — set region at project level in the UI under Cloud compute Functions Region . After changing region, redeploy . Function level region beats the site level UI setting. Memory / vCPU ⚠️ Do NOT set config.memory or config.vcpu speculatively — billing scales linearly with size. When justified (e.g. observed OOM processing large PDFs): memory : 1024–4096 MB. vcpu : 0.5–2.0 (0.5 → 1024 MB, 2.0 → 4096 MB). Mutually exclusive; Netlify sizes the other automatically. Needs Credit based Pro/Enterprise. Via netlify.toml : [functions.heavy]\n memory = "2gb" . Bundling & files on disk ⚠️ Files read via fs.readFile at runtime are not bundled (ENOENT in production). Import static data as a module, or declare it: ⚠️ Combined env vars are capped at ~4 KB for ALL functions — keep PEM keys/service account JSON out of env vars. JS only esbuild: [functions]\n node bundler = "esbuild" . Limits (not configurable) Synchronous execution: 60s . Scheduled: 30s . Background: 15 min . Buffered request/response payload: 6 MB (binary is Base64 encoded, ~30% overhead → effective 4.5 MB binary limit). Streamed response: 20 MB . Background payload: 256 KB . Local testing & deploy Most frameworks emulate functions in their dev server. Vite frameworks (Astro, Nuxt, TanStack Start, React Router): install @netlify/vite plugin and run the dev server. Next.js and anything else: use the [Netlify CLI](https://docs.netlify.com/api and cli guides/cli guides/local development/) ( netlify dev ). Scheduled functions don't fire on a schedule locally — invoke once with netlify functions:invoke <name . Deploy: push to Git for continuous deployment, or use the Netlify CLI/API. Logs & metrics live in the Netlify UI; stream with the CLI. All deployed function versions appear under the Functions tab; use the search field at the top of the list to filter functions by name, and the separate filter to select a branch or enter a Deploy Preview number. Node runtime version Runtime follows the build's Node.js version (fallback: Node.js 24). Override by setting env var AWS LAMBDA JS RUNTIME (e.g. nodejs24.x ) via UI/CLI/API — not netlify.toml — then redeploy. ES modules: dirname / filename unavailable, use import.meta.url ; named imports of CommonJS packages fail, use a default import. Legacy / Go (avoid unless needed) Go must use the [Lambda compatible API](https://docs.netlify.com/build/functions/lambda compatibility/?fn language=go); Go routing/region/memory are set in netlify.toml . For migrating Lambda style JS/TS, @netlify/aws lambda compat wraps an AWS handler: <! Gap: docs only show the .js/.ts same name precedence; unstated for .mjs/.mts, .cjs/.cts, or subdirectory functions. <! system: agent context/functions/system.md — human owned, merged by ctx gen; edit system.md, not this section Netlify house rules (functions) These are org conventions, not docs facts — they are merged into the rendered skill by ctx gen and are never generated. Extracted from the previous hand written netlify functions skill; owned by the skills maintainer. 1. Use TypeScript ( .mts ) when possible. 2. Access environment variables via Netlify.env.get() (prefer it over process.env for consistency). 3. Never add CORS headers unless explicitly requested. 4. Store secrets in environment variables, never in code. 5. context.geo and context.ip are mocked under netlify dev — placeholder values, not the real location or client IP. Don't conclude geo code is broken because local values never change; exercise branches with netlify dev geo=mock country=DE and verify on a deploy. 6. Do NOT set config.memory or config.vcpu speculatively. Raise them only for known memory/compute intensive work or observed OOM/timeouts caused by the function's own work — billing sca