On-demand Builders

On-demand Builders are serverless functions used to generate web content as needed that’s automatically cached on Netlify’s Edge CDN. They enable you to build pages for your site when a user visits them for the first time and then cache them at the edge for subsequent visits.

Key characteristics of On-demand Builders:

  • They accept GET requests only.
  • They don’t provide access to HTTP headers or query parameters from incoming requests.
  • They are protected from repeated invocations by caching generated pages or assets.
  • Response caching is based on the URL path and can’t be customized.
  • A new site deploy invalidates the cached responses associated with builders in the same deploy context, such as the same Deploy Preview number or the same branch deploy.

On-demand Builders are the recommended primitive to achieve Distributed Persistent Rendering (DPR) on Netlify. They also enable support for Incremental Static Regeneration (ISR) for Next.js sites on Netlify. You can join the conversation about DPR and ISR in our Support Forums.

# Create On-demand Builders

To get started, follow our docs to create a function in TypeScript or in JavaScript. Use the synchronous function format, and return a response that can be served as a cached static asset. To convert the function to a builder, use the builder() method from the @netlify/functions package.

First, add the package to your site dependencies:

npm install -D @netlify/functions

Then, pass your function as a parameter to the builder() method and export this method as the handler from your function file.

import { builder, Handler } from "@netlify/functions";

const myHandler: Handler = async (event, context) => {
  // logic to generate the required content
};

const handler = builder(myHandler);

export { handler };
const { builder } = require("@netlify/functions")

async function handler(event, context) {
  // logic to generate the required content
}

exports.handler = builder(handler);

For example, your builder could return a full HTML page or a processed image file. Here’s a “hello world” example:

import { builder, Handler } from "@netlify/functions";

const myHandler: Handler = async (event, context) => {
  return {
    statusCode: 200,
    headers: {
      "Content-Type": "text/html",
    },
    body: `
    <!DOCTYPE html>
      <html>
        <body>
          Hello World
        </body>
    </html>
    `,
  };
};
const handler = builder(myHandler);

export { handler };
const { builder } = require("@netlify/functions")

async function handler(event, context) {
  return {
    statusCode: 200,
    headers: {
      "Content-Type": "text/html",
    },
    body: `
    <!DOCTYPE html>
      <html>
        <body>
          Hello World
        </body>
    </html>
    `,
  };
}

exports.handler = builder(handler);

On success, the builder returns a 200 status code and an HTML page with a “Hello World” message.

For a working example of using On-demand builders with code to explore, you can visit this example site and its corresponding code repository.

# Time to live (TTL)

On-demand Builders support an optional time to live (TTL) pattern that allows you to set a fixed duration of time after which a cached builder response is invalidated. This allows you to force a refresh of a builder-generated response without a new deploy.

You can set a TTL for your builder by including a ttl, in seconds, in your response. ttl has a minimum value of 60.

import { builder, Handler } from "@netlify/functions";

const originalResponse = {
  body: ':thumbsup:',
  statusCode: 200,
  ttl: 3600,
};

const myHandler: Handler = async (event, context) => {
  // logic to generate the required content

  return originalResponse;
};

const handler = builder(myHandler);

export { handler };
const { builder } = require("@netlify/functions")

const originalResponse = {
  body: ':thumbsup:',
  statusCode: 200,
  ttl: 3600,
}

async function handler(event, context) {
  // logic to generate the required content

  return originalResponse
}

exports.handler = builder(handler);

After the TTL has expired, the next request triggers a builder invocation to regenerate the content. The previously cached response is served for 60 seconds after a refresh is triggered.

# Use On-demand Builders

Similar to other functions, On-demand Builders can be invoked at endpoints relative to the base URL of your site. Builders, however, use a unique builders path for invocation: /.netlify/builders/FUNCTION_NAME.

Builder endpoints can be called directly like regular web URLs or can be executed by redirecting traffic from another URL.

For example, consider a news site with a large amount of infrequently visited, archived content. You can use a builder to generate those less-frequented pages on-demand when a user requests them. The request URL might be something like this:

https://myawesomenews.com/.netlify/builders/my-archive-builder/article/199x-news

The first call to the builder invokes it and returns the generated page, assuming that’s the logic in your function. Subsequent calls return the cached page content until the next deploy or until the time to live (TTL) has elapsed.

In another example, if you want to replace a static image src with a custom-sized image generated using a builder, you can format an img tag like this:

<img alt="kittens." src="/.netlify/builders/my-image-transformer/file/kittens.jpg/width/640">

The first builder call processes and returns the image, and subsequent calls return the cached image until the next deploy or until the TTL has elapsed.

# Customize paths with redirects

In the example above, you might prefer to use a different URL instead of the default builder endpoint. You can do this by redirecting traffic from a URL to the builder using redirect rules in the _redirects file or netlify.toml Netlify configuration file.

Here are some examples with _redirects file and netlify.toml syntax.

/images/*  /.netlify/builders/my-image-transformer   200

/archive   /.netlify/builders/my-archive-builder  200
[[redirects]]
  from = "/images/*"
  to = "/.netlify/builders/my-image-transformer"
  status = 200

[[redirects]]
  from = "/archive"
  to = "/.netlify/builders/my-archive-builder"
  status = 200

The original path is passed to the builder as part of the event object. To continue an example from above, a call to /images/kittens.jpg/width/640 triggers the my-image-transformer function with the following values passed to it as part of the event:

{ 
  "path": "/images/kittens.jpg/width/640",
  "httpMethod": "GET"
  ...
}

For more information about the event parameter and the Netlify Functions programming model, refer to our docs on synchronous function format for TypeScript or JavaScript.

# Redirect workaround for query parameters

On-demand Builders currently don’t process or cache query parameters from redirected paths. In most cases, it’s preferable to embed these parameters into the path directly, but in cases where you don’t control the path, you can re-shape it with an additional redirect rule to the path resolution. The first rule must have a 3XX HTTP status code, as that ensures a new URL for our redirects engine to process the second rule, passing your new path to the builder. Here’s an example in _redirects syntax:

/generated/image url=:url w=:w /img/:url/:w 301! 

/img/:url/:w /.netlify/builders/image 200

# Shadowing

Redirect rules leverage shadowing, which means that they trigger only if there isn’t already an asset at the specified from path. This can be useful if your prebuilt assets and content generated by your builder share the same path.

If you prefer to trigger the proxy regardless of the presence of a pre-built asset, you can append ! to the status code in the _redirects file, or add force = true in netlify.toml.

# Local development

You can build and test logic like any other function using Netlify Dev. When testing locally, however, caching isn’t available.

# Secure On-demand Builders

You can use the same authentication and authorization strategy as the rest of your Netlify site to secure builders. We recommend using JSON Web Tokens (JWT), roles, and redirect rules for managing granular access. Refer to our docs on role-based access control to learn more.

# Limits

  • The maximum response payload size for a builder is 6 MB.
  • Pages or assets generated by On-demand Builders are limited to 10,000 per deploy. For a higher limit, please contact sales. If the limit is reached, additional generated pages or assets will have reduced caching and may be regenerated upon subsequent requests.

# Usage and billing

Requests to builders that return a previously non-cached response count towards your Netlify Functions usage for billing purposes. This includes requests per month and run time per month. If a cached response is available and served, no function invocation is made and no usage is incurred against your Netlify Functions allotment.

On-demand Builders execution doesn’t count towards build minutes.