Skip to content
Deployment
Skill

/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,

From plugin
netlify-skills
3715 skills1 MCP
Install
$ npx -y skills add netlify/context-and-tools --skill netlify-edge-functions --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/netlify-edge-functions

Context preview

The summary Claude sees to decide when to auto-load this skill.

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,

SKILL.md

netlify-edge-functions.SKILL.md
name: netlify-edge-functions
description: 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 edge, or transforming responses — or when configuring path routing, response caching, or edge error handling. Triggers on tasks like "add auth middleware", "geo-based redirect", "A/B testing at the edge", "rewrite requests", or editing files in netlify/edge-functions.

Netlify Edge Functions

**Reach for this (modern):** default-export handler + inline `config` export with a narrowly-scoped `path`. Import types from `@netlify/edge-functions`.

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

export default async (request: Request, context: Context) => {
  // return Response | URL (rewrite) | undefined (continue chain)
};

export const config: Config = { path: "/products/*" };

**Avoid:** import maps in `deno.json` (unsupported — use a separate file via `deno_import_map`). Do not hand-write a function your framework's adapter already generates (Next.js, Astro, Remix, SvelteKit, Nuxt, etc.) — check the framework adapter/reference first; duplicating adapter middleware causes conflicts.

File location

  • Default directory: `YOUR_BASE_DIRECTORY/netlify/edge-functions`.
  • Custom directory: `edge_functions` key under `[build]` in `netlify.toml`. Keep it **outside** the publish directory so source files aren't deployed.
  • `.js`/`.ts`/`.jsx`/`.tsx` all supported. If a `.ts` and `.js` file share a name, the `.ts` is ignored and the `.js` deploys.

⚠️ A function without a route silently never runs

Edge functions are **not** auto-assigned a URL. No `config` export and no `netlify.toml` declaration = deploys clean, no build error, no warning, never executes. If "my edge function does nothing," check the route first.

Request handling patterns

Handler receives `(request: Request, context: Context)`. Return one of:

  • `Response` — respond directly (ends the chain; declared redirects for the path do not run)
  • `URL` — rewrite to a **same-site** URL with 200 status (address bar unchanged)
  • `undefined` / empty `return;` — bypass this function, continue the chain

Netlify adds no headers to edge requests — use `context` for client info.

Redirect

export default async (req: Request, { cookies, geo }: Context) => {
  if (geo.city === "Paris" && cookies.get("promo-code") === "15-for-followers") {
    return Response.redirect(new URL("/subscriber-sale", req.url));
  }
};

Rewrite (same-site only)

export default async (request: Request, { geo }: Context) => {
  if (geo.city === "Paris") return new URL("/subscriber-sale", request.url);
};

To reach another site or external content, use `fetch()` — rewrite via `URL` is same-site only.

Middleware transform

import type { Context } from "@netlify/edge-functions";

export default async (request: Request, context: Context) => {
  const response = await context.next();
  const text = await response.text();
  return new Response(text.toUpperCase(), response);
};

`context.next()` runs the rest of the chain and returns the origin `Response`. Only call it if you need the response body (it costs latency otherwise).

To transform a **different** path, use `fetch()` — but this starts a **new** request chain and re-runs any edge functions matching that path. Use `context.next()` to hit a static asset/serverless function at the same internal path without re-running edge functions.

Read the request body

A body can only be read once. If you read it, pass a fresh request to `next()`:

export default async (req: Request, context: Context) => {
  const body = await req.json();
  if (!isValid(body.access_token)) return new Response("forbidden", { status: 403 });
  return context.next(new Request(req, { body: JSON.stringify(body) }));
};

Conditional requests

`next()` normally forces a full response. For client caching control:

const res = await next({ sendConditionalRequest: true });
if (res.status === 304) return res;

`Context` object

  • **`geo`** — `city`, `country {code,name}`, `subdivision {code,name}`, `latitude`, `longitude`, `timezone`, `postalCode`.
  • **`cookies`** — `get(name)`, `set(options)`, `delete(name|options)` (CookieStore web standard). ⚠️ Cross-subdomain cookies require a **custom domain** — `netlify.app` is on the Public Suffix List.
  • **`next(options?)` / `next(request, options?)`** — continue the chain; `options.sendConditionalRequest`.
  • **`params`** — path params, e.g. `/pets/:name` → `{ name: "winter" }`. Query string: use `request.url`.
  • **`ip`**, **`requestId`**, **`server.region`**.
  • **`site`** — `id`, `name`, `url`. **`account.id`**. **`deploy`** — `context`, `id`, `published`, `skewProtectionToken`.
  • **`waitUntil(promise)`** — run work after the response is sent (analytics, logs) without blocking it. Still subject to the CPU time limit.

`Netlify.context` gives the same context inside the handler (`null` outside it).

Environment variables

Access via `Netlify.env.get(name)` (also `has`, `set`, `delete`, `toObject`). `set`/`delete` are invocation-scoped only — they do **not** persist; use the Netlify env API to update.

const value = Netlify.env.get("MY_IMPORTANT_VARIABLE");

⚠️ **Gotchas:**

  • Variables in `netlify.toml` are **NOT** available to edge functions.
  • Scope must include **Functions** to reach runtime. **Build**-scoped vars are build-only — embed them at build time if needed.
  • Values are frozen at deploy time. Change a var → new deploy required. Deploy Previews/branch deploys use their deploy-time values.

Configuration / routing

Config via inline `config` export or `netlify.toml`. Properties:

  • **`path`** — `URLPattern` string or array; must start with `/`. e.g. `["/",
Read more
Ships withnetlify-skills

Public Netlify skills for AI coding agents. Each skill is a focused, factual reference for a Netlify platform primitive — designed to help agents build correctly on Netlify without needing to search docs.

Get the whole plugin

Other skills on netlify-skills.