News & Announcements

Netlify Functions, designed for Agent Experience

Netlify Functions, designed for Agent Experience

We first launched Netlify Functions in 2019. What started as a backend-for-frontend companion quickly grew into a true backend powerhouse, now handling hundreds of millions of requests every day across an ever-expanding range of use cases.

In 2023, we announced Netlify Functions 2.0, where we completely reimagined the experience of the product and built a brand new API surface with one clear focus: Developer Experience.

Today, we’re shipping a set of features and improvements that take Netlify Functions to the next frontier: Agent Experience.

We took the learnings and the feedback we got from our users and we’ve designed these new capabilities to be inherently agent-friendly, making it easy for AI agents to discover, consume, and interact with them, so builders can unlock more value with less effort.

Event-triggered functions

Responding to a web request is by far the most popular use case of functions, but did you know that you can also use them to run custom logic when different events occur on your Netlify project?

To name just a few examples, you can configure a function to run when a new deploy starts building or when it reaches a specific end state; when you receive a new form submission; or when a user signs up or logs in to your application, even letting you block access or mutate the user object based on your business logic.

This functionality has existed for a while, but the experience wasn’t really on par with the rest of the platform. Subscribing to these events meant creating your function files with specific names that are easy to forget by humans and hard to discover by agents, while consuming their payloads required manually parsing the body of an HTTP request and extracting the right data.

Starting today, any function can subscribe to any platform event by exporting the corresponding event handler from its default export object. Each handler receives an event with the corresponding event payload, and the return value is automatically processed into an action, when applicable.

The arguments and return values of all event handlers are fully typed, making them easier to work with by humans and their IDEs, as well as by AI coding agents.

import { UserLoginEvent, UserSignupEvent } from "@netlify/functions"
const isAllowedEmail = (email: string) => email.endsWith("@netlify.com")
export default {
userSignup(event: UserSignupEvent) {
if (!isAllowedEmail(event.user.email)) {
return event.deny()
}
// Adding metadata to the user. In this case, we're setting
// a custom `onboarded` property, which will be set to `true`
// by another part of our application once the user completes
// their onboarding process.
return {
user: {
...event.user,
appMetadata: {
...event.user.appMetadata,
onboarded: false,
}
}
}
},
userLogin(event: UserLoginEvent) {
if (!isAllowedEmail(event.user.email)) {
return event.deny()
}
if (!event.user.appMetadata.onboarded) {
return event.deny()
}
}
}

A single function can subscribe to multiple events by declaring the corresponding handlers. Conversely, it is also possible to subscribe to the same event with multiple functions declaring the same handler.

This lets you structure your code according to what functionally belongs together, without arbitrary conventions or restrictions imposed by the platform.

Fetchable module support

The fetchable module interface is a convention used across modern JavaScript runtimes (like Deno and Bun) and by many of the web frameworks built on top of them (like Hono or Nitro). Netlify Functions now support it natively, so code targeting this convention can run on Netlify with minimal changes and you get interoperability with the broader ecosystem.

Adopting the same shape also lets a single function respond to web requests and subscribe to platform events in the same module: you define the HTTP handler logic in a fetch handler, and any platform event handlers (deploySucceeded, userLogin, formSubmitted, etc.) as named methods on the same default export object.

For convenience, you can also define the function’s configuration block as a config property in the same object and use the NetlifyFunction type as the overarching type that ties everything together.

The code below shows an example of a function that takes user-submitted feedback from two sources (a third-party application hitting an HTTP endpoint as well as a feedback page built with Netlify Forms) and classifies it using the same logic.

import { NetlifyFunction } from "@netlify/functions"
import OpenAI from "openai"
const client = new OpenAI()
const processFeedback = async (text: string) => {
const response = await client.responses.create({
model: "gpt-4.1-mini",
input: `Classify the sentiment of this text as "positive" or "negative":\n\n${text}`,
})
console.log(`We've received ${response.output_text} feedback.`)
}
export default {
async fetch(req, context) {
const feedback = await req.text()
await processFeedback(feedback)
return new Response("Thank you!")
},
config: {
method: "POST",
path: "/api/my-webhook",
},
formSubmitted(event) {
const { feedback } = event.data
await processFeedback(feedback)
}
} satisfies NetlifyFunction

No migration needed

It’s important to note that these changes are purely additive and do not require any migration on your part. The existing signature of a default export function and a named config export will continue to work and will not be deprecated.

The following two implementations are equivalent and can be used interchangeably:

import { Config, Context } from "@netlify/functions"
export default async (req: Request, context: Context) => {
return new Response("Hello world")
}
export const config: Config = {
path: "/hello"
}
import { NetlifyFunction } from "@netlify/functions"
export default {
fetch: (req, context) => {
return new Response("Hello world")
},
config: {
path: "/hello"
}
} satisfies NetlifyFunction

New background functions syntax

Background Functions are an incredibly powerful tool for long-running tasks, allowing up to 15 minutes of execution time.

However, declaring a background function involved naming your function files with the -background suffix, which makes the feature unintuitive and hard to discover, for both humans and agents.

It is now possible to define a background function by setting background: true in the configuration object, with no restrictions on the name of the file.

Declaring background functions with the -background suffix will continue to work for backwards-compatibility, so you don’t need to migrate any of your existing functions.

import { Config, Context } from "@netlify/functions"
export default async (req: Request, context: Context) => {
console.log("Hello from a background function")
}
export const config: Config = {
background: true,
path: "/hello-background"
}

Region selection

Netlify gives you full control over the geographical location of your functions.

This is crucial for performance, letting you run your code close to your key audiences and to any databases or services it interacts with. For some users, this is also important for compliance and data sovereignty reasons.

The functions region had been available in the Netlify UI and applies globally to all functions.

As of today, it is possible to define it in code, on a per-function basis. This makes it easier for agents to set this configuration on your behalf, and lets you use deploy previews to experiment and validate the right configuration for you before you apply it to your live site.

import { Config, Context } from "@netlify/functions"
export default async (req: Request, context: Context) => {
return new Response("Hello from Frankfurt 🇩🇪")
}
export const config: Config = {
path: "/hello",
region: "fra"
}

Refer to the documentation for a full list of supported regions. The Config type from @netlify/functions has been updated with all the regions, so you can also just ask your agent to set one for you.

Memory and CPU configuration

We see developers building larger and more complex applications than we’ve ever seen on Netlify. Some of these workloads need more resources to run reliably, especially when they involve processing large payloads or running AI inference.

Starting today, you can give your functions more compute resources.

To configure the memory allocation, set the new memory property, with a numeric megabyte count or a string value with MB or GB units. You can choose any value between 1 GB (the default) and 4 GB.

import { Config, Context } from "@netlify/functions"
export default async (req: Request, context: Context) => {
return new Response("Hello with more compute")
}
export const config: Config = {
memory: "2gb",
path: "/hello-memory"
}

It is also possible to allocate more CPU by specifying the number of virtual CPUs (vCPUs) available to the function. You can choose any value between 0.5 (the default) and 2.

import { Config, Context } from "@netlify/functions"
export default async (req: Request, context: Context) => {
return new Response("Hello with more compute")
}
export const config: Config = {
path: "/hello-vcpu",
vcpu: 1.5
}

Both settings scale together: increasing the memory will also increase the vCPU count and vice-versa. For this reason, you can only specify one of the two — whatever is more relevant to your application or easiest for you to reason about — and we’ll adjust the other accordingly.

Configuring memory and vCPU is only available on credit-based plans with Pro and above. Increasing the resources available to functions will increase your compute credits consumption.

getContext import

The Netlify context object exposes various properties about the request client, such as their geolocation and IP address, as well as metadata about your account, project and deploy. It also offers a set of utilities for manipulating cookies and creating deferred tasks.

This object is available as the second argument of your function’s HTTP handler, but you may find yourself in a situation where you’re trying to access this data from within a different scope, where you can’t directly access the original handler arguments. This is often the case when a web framework or another abstraction layer sits between the code you’re writing and the underlying function handler.

You could already do this by reading the Netlify.context global, but a global is hard to discover, both for humans browsing autocomplete and for AI agents reading the code. The new getContext import from @netlify/functions does the same thing as a named import, which makes it far easier to surface in tooling.

Call it from any scope that is a child of the request handler:

import { getContext } from "@netlify/functions"
const getResponseFromDifferentScope = () => {
const city = getContext().geo?.city ?? "somewhere"
return new Response(`Hello from ${city}`)
}
export default async () => {
const response = getResponseFromDifferentScope()
return response
}
export const config: Config = {
path: "/hello"
}

Wrapping up

Netlify’s compute primitives are constantly evolving. Functions 2.0 was a major leap in developer experience, introducing new APIs built from the ground up to help developers achieve more with less effort.

This release builds on that foundation, introducing new capabilities while improving existing ones, with a clear focus on Agent Experience. It’s designed to help you and your AI agents get the most out of our platform so you can build more, better, and faster.

And we’re far from done. We’ve been hard at work on a next-generation compute platform that will unlock an entirely new category of use cases. We can’t wait to share it with you and to see what you build next.


Keep reading

Recent posts

How do the best dev and marketing teams work together?