netlify-edge-functions

Write, configure, and deploy Netlify Edge Functions (Deno runtime at the network edge) in TypeScript/JavaScript. Use when adding request/response manipulation at the edge — auth middleware, geolocation redirects, A/B testing and personalization, content localization, redirects/rewrites, SSR at the e

By netlify · 2,283 installs

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

Source repository · Upstream listing

Netlify Edge Functions Use this form One file = one function: a default export handler plus a config export with the route. Alternatively, declare the route in netlify.toml . Request and Response are global. Import Context and Config types from @netlify/edge functions . Read env vars with Netlify.env.get() . process.env works only for Next.js middleware. Avoid these: A file with no config.path or pattern and no [[edge functions]] entry. It deploys but never runs. Import maps in deno.json . They are unsupported. Use a separate file with [functions] deno import map . Variables set in netlify.toml . They are invisible to edge functions. Netlify.env.set() or Netlify.env.delete() as persistence. They affect only the current invocation. fetch() to the same site for the current path. Use context.next() instead. ⚠️ Footguns No route = silent no op. Edge functions get no automatic URL. path: "/ " runs on every request , including assets. Narrow it, or add excludedPath . Caching needs both parts: cache: "manual" in config AND cache headers on the returned Response . With only one, nothing is cached. Cached functions shadow static files. A cached function on / serves /cat.png instead of the file. Returning a Response ends the chain. Redirect rules for that path never run. Same site fetch() and URL rewrites start a new request chain. Matching edge functions run again. Use context.next() to reach the static asset or serverless function at the same path. The request body can be read once. After await req.json() , pass new Request(req, { body }) to context.next() . Same named foo.ts and foo.js : the TS file is ignored and the JS file deploys. 50 ms CPU per request , including waitUntil work. Not executed on sites with Split Testing enabled. Custom Headers and basic auth headers do not apply to edge functions. Not supported on HIPAA compliant hosting. netlify.app is on the Public Suffix List. Cross subdomain cookies need a custom domain. Files What Where Functions <base /netlify/edge functions/ .{js,ts,jsx,tsx} Custom dir [build] edge functions = "my dir" (relative to base dir; keep it outside the publish dir) Name in toml file name without extension ( auth.ts → function = "auth" ) Import map any JSON file, referenced by [functions] deno import map = "./import map.json" Return values Return Effect Response sent to client; chain ends URL same site rewrite, 200, address bar unchanged undefined / return; skip this function; chain continues Endpoint Redirect / rewrite Rewrites must target the same site. To reach other sites, use fetch . Middleware (modify origin response) Call context.next() only when you need the response body. If you just return undefined , the chain continues on its own. Conditional requests By default, next() strips conditional headers, so it always returns a full response. Read body, then continue Netlify user gate (private / team login protected projects) Post response work waitUntil does not block the response, but it still counts against the 50 ms CPU limit. Streaming SSR ( .tsx ) Context Property Notes cookies.get(name) / .set(options) / .delete(name \ options) set and delete use CookieStore option format; they act on the outgoing response geo city , country.{code,name} , subdivision.{code,name} , latitude , longitude , timezone , postalCode (format varies) ip client IP next(options?) / next(request, options?) Promise<Response from the next item in the chain params URLPattern named groups from path ( /pets/:name → { name } ). For the query string, use request.url user { id, email?, expiresAt } or undefined . It is a Netlify account, not Netlify Identity; for Identity, use getUser() from @netlify/identity waitUntil(promise) runs after the response is sent requestId , account.id , server.region — site { id, name, url } deploy { context, id, published, skewProtectionToken } Netlify adds no client info headers to the request. Read client info from context instead. Netlify.context gives the same object, but only inside the handler. Outside it, it is null . Netlify.env : get(name) , has(name) , toObject() , and invocation scoped set and delete . To change variables persistently, use the [env API](https://open api.netlify.com/ tag/environmentVariables). Web APIs: console , atob , btoa fetch , Request , Response , URL , File , Blob TextEncoder / TextDecoder (and their Stream variants), Performance Web Crypto: randomUUID , getRandomValues , SubtleCrypto WebSocket setTimeout , clearTimeout , setInterval Streams: ReadableStream , WritableStream , TransformStream URLPattern Modules: Node built ins: use the node: prefix ( import { randomBytes } from "node:crypto" ). Deno modules: import by URL, or through an import map. npm packages (beta): import by name. Packages with native binaries or runtime dynamic imports may break. Declarations Inline config Key Value path URLPattern string or array; must start with / excludedPath URLPattern string or array; must start with / pattern / excludedPattern regex alternatives to path / excludedPath method HTTP method string or array header { name: true \ false \ "regex" } ; case insensitive names; multi value headers match the comma joined list onError "fail" (default), "/custom path" , or "bypass" cache "manual" The netlify agent category request header maps any user agent to a [client category](https://docs.netlify.com/build/user agent categories). netlify.toml The toml form supports function , path , excludedPath , pattern , excludedPattern , [edge functions.header] , and cache . Set onError inline. Use toml when you need explicit ordering of several functions on one path. A request to /admin runs auth , then common , then injector . Order Netlify walks the following order twice for the request path. The first pass runs only functions without caching configured. The second pass runs only cached functions. 1. Framework generated functions declared in a config file. 2. Your netlify.toml declarations, top to bottom. 3. Framework and integration generated inline declarations. 4. Your inline declarations, alphabetical by file name. If the same function is declared both in toml and inline, the configs are merged and treated as step 4; inline wins for duplicate fields. After the edge functions, redirect rules run, unless a function already returned a Response . A redirect to a new path repeats the whole process for that path. An edge function declared on the target of a static rewrite does not run for rewritten requests. Response caching Headers must be set in the function code. Supported headers: Cache Control , CDN Cache Control , Netlify CDN Cache Control Expires (overridden by max age / s maxage ) Vary , Netlify Vary See [caching overview](https://docs.netlify.com/build/caching/caching overview). A new deploy in the same deploy context invalidates cached responses. Cached hits skip invocation, and they do not count toward invocation limits. Cache reusable endpoint output, such as SSR. Don't cache routing or personalization middleware, such as cookie based product ordering. Local dev has no cache. Cache headers are ignored locally. Error handling ( onError ) Value Effect "fail" (default) chain stops; generic error page is shown "/unavailable" rewrite to that same site path; edge functions on it are not invoked "bypass" skip the failing function; chain continues Fail closed for critical logic: use the default for internal auth, or a custom path for customer facing auth. Fail open ( bypass ) for nice to haves such as localization. Environment variables Variables must have the Functions scope. Build scoped variables exist only at build time; embed their values during the build if needed. Changing a variable requires a new deploy. Deploy info (site, region, deploy id) comes from context , not from env vars. Local dev / deploy / logs Run npm install netlify cli g , then netlify dev . Visit http://localhost:8888/<path . Changes apply on the next request. Debug with edge inspect or edge inspect brk . Mock location with geo=mock (San Francisco), or geo=mock country=DE . context.user is undefined locally. Manual deploys require CLI ≥ 12.2.8 . Deploys are atomic, and Deploy Previews and rollbacks include edge functions. Logs: in the Netlify UI, go to Cloud compute Edge functions ( https://app.netlify.com/projects/{site name}/edge functions ). For older deploys: Deploys → open the deploy → Edge Functions . console.log output is tagged with the function name. Retention is ≥ 24 h (7 days on some plans). [Log Drains](https://docs.netlify.com/manage/monitoring/log drains) are available on Enterprise plans. Limits Limit Value Bundle size 20 MB compressed Memory (per set of deployed functions) 512 MB CPU time per request 50 ms (excludes I/O wait) Response header timeout 40 s Prerendering does not apply to paths served by an edge function. Multiple framework plugins that generate edge functions may collide. Docs: [API](https://docs.netlify.com/build/edge functions/api.md) · [Declarations](https://docs.netlify.com/build/edge functions/declarations.md) · [Optional configuration](https://docs.netlify.com/build/edge functions/optional configuration.md) · [Limits](https://docs.netlify.com/build/edge functions/limits.md) · [Examples](https://edge functions examples.netlify.app/) <! Gaps: no legacy/deprecated handler form is documented; whether method works in netlify.toml is unstated. <! Undocumented: context.params with regex pattern declarations; monthly invocation limits per plan. <! system: agent context/edge functions/system.md — human owned, merged by ctx gen; edit system.md, not this section Netlify house rules (edge functions) These are org conventions and field learned guardrails, not docs facts — they are merged into the rendered skill by ctx gen and are never generated. Extracted from the previous hand written netlify edge functions skill; owned by the skills maintainer. 1. Check the framework's adapter/reference first: a custom edge function that duplicates adapter generated middleware causes conflicts. Only hand write an edge function when the framework doesn't already generate one for the job. 2. Scope path narrowly. path: "/ " intercepts every request — including static assets — adding latency to each one and billing an edge invocation for it. 3. An edge function without a route (no config export, no netlify.toml declaration) still deploys, but silently never runs: no build error, no warning. When "my edge function does nothing", check the route first. 4. Choose edge vs serverless by workload shape: edge functions for low latency request/response manipulation, geolocation logic, auth checks/redirects, and A/B personalization; serverless functions for long running work (up to 15 min), heavy Node.js dependencies, database heavy operations, background/scheduled tasks, or memory needs above 512 MB. 5. Cache headers on an edge response do nothing without cache: "manual" in config — it's both or neither. Setting Cache Control on the returned Response has no effect unless the function also opts in. 6. When explaining declaration processing order, state the two pass loop, not just the ordering: