Values and methods for build event handlers
Build event handlers have access to the following values and methods.
Build event handler values
constants
: values generated by the build eventbuildConfig
: values configured by the extension usernetlifyConfig
: the site’s Netlify configurationpackageJson
: the contents of the site’spackage.json
file- environment variables: values available in the Netlify build environment
constants
Each event includes a constants
key.
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
extension.addBuildEventHandler("onPreBuild", ({ constants }) => { console.log(`The site id is ${constants.SITE_ID}`);});
The constants
key contains the following values:
CONFIG_PATH
: path to the Netlify configuration file.undefined
if none was used.PUBLISH_DIR
: directory that contains the deploy-ready HTML files and assets generated by the build. Its value is always defined, but the target might not have been created yet.FUNCTIONS_SRC
: directory where function source code lives.undefined
if nonetlify/functions
directory exists in the base directory and if not specified by the user.FUNCTIONS_DIST
: directory where built serverless functions are placed before deployment. Its value is always defined, but the target might not have been created yet.IS_LOCAL
: boolean indicating whether the build was run locally or on Netlify.NETLIFY_BUILD_VERSION
: version of Netlify Build as amajor.minor.patch
string.SITE_ID
: Netlify site ID.
Along with these constants, build event handlers can also access any of the environment variables that are available in the build environment.
buildConfig
If your build event handler requires additional values from the user, you can specify these requirements with a configuration schema that defines what kind of values these should be. These requirements are defined using Zod. Users can then supply the required values in their netlify.toml
configuration file. These values can be used inside of your build event handler, or to create dynamic build event handlers.
Try the extension UI
Instead of using buildConfig
and asking users to configure your extension in their
netlify.toml
file, you can instead use the extension UI to let
a user configure your extension from the Netlify UI.
Make a configuration schema
To make a schema using Zod, import { z } from "@netlify/sdk"
.
Then, use z.object
to define what kind of values you expect from the user of your extension. For options, refer to the Zod documentation. Zod automatically ensures that the values a user provides are valid. It also adds typing, so you can be confident while you use the values in your buildConfig
object.
import { NetlifyExtension, z } from "@netlify/sdk";
const buildConfigSchema = z.object({ output_path: z.string().optional(), fail_deploy_on_score_thresholds: z.boolean().default(false), thresholds: z .object({ performance: z.number(), accessibility: z.number(), "best-practices": z.number(), seo: z.number(), pwa: z.number(), }) .optional(),});
const extension = new NetlifyExtension({ buildConfigSchema });
extension.addBuildEventHandler("onPreBuild", ({ buildConfig }) => { console.log(buildConfig.output_path); console.log(buildConfig.thresholds);});
A user then supplies the required values in their site’s netlify.toml
file:
[[integrations]] name = "extension-slug"
[integrations.config] output_path = "reports/lighthouse.html" fail_deploy_on_score_thresholds = "true"
[integrations.config.thresholds] performance = 0.9 accessibility = 0.9 best-practices = 0.9 seo = 0.9 pwa = 0.9
These values are passed into the build event handler’s buildConfig
when the build handler is executed.
Dynamic build event handlers
It is possible to hook into Netlify’s build events based on the values in the buildConfig
.
To do this, pass an object to the addBuildEventHandler
method as the third parameter. The object should contain an if
property that resolves to a boolean value. For example, the if
property could contain a method that checks buildConfig
for a specific value. The build event handler will only run when the if
property evaluates to true
.
import { NetlifyExtension, z } from "@netlify/sdk";
const buildConfigSchema = z.object({ output_path: z.boolean().optional(),});
const extension = new NetlifyExtension({ buildConfigSchema });
extension.addBuildEventHandler( "onPreBuild", ({ buildConfig }) => { console.log("Hello world from onPreBuild event!"); }, { if: (buildConfig) => buildConfig.before === true, });
extension.addBuildEventHandler("onPostBuild", () => { console.log("Hello world from onPostBuild event!");});
A user then supplies the required values in their site’s netlify.toml
file:
[[integrations]] name = "extension-slug"
[integrations.config] before = true
In this example, both onPreBuild
and onPostBuild
build event handlers will run.
netlifyConfig
When an event executes, a site’s Netlify configuration is normalized by @netlify/config and passed to your build event handler as a netlifyConfig
object. Normalization includes applying context-specific or branch-specific settings and combining settings from netlify.toml
with build settings configured in the Netlify UI.
After normalization, build event handlers can access and modify most netlifyConfig
properties during a site’s build. These include redirects, headers, and build configuration. If a site doesn’t use netlify.toml
or build settings selections in the Netlify UI, netlifyConfig
and its properties contain default build settings.
Here’s a list of modifiable properties:
redirects
: array of redirects with their modifiable optionsheaders
: array of headers with their modifiable optionsfunctions
: object with options for modifying functionsfunctions.directory
: string that includes the path to a site’s functions directoryedge_functions
: array of edge functions with their modifiable optionsbuild.command
: string that includes a site’s build commandbuild.environment
: object that contains a subset of a site’s environment variablesbuild.edge_functions
: string that includes the path to a site’s edge functions directorybuild.processing
: object that includes options for post processing HTML.
Here’s a build event handler code sample that modifies several of the above properties.
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
extension.addBuildEventHandler("onPreBuild", ({ netlifyConfig }) => { const newCommand = `node YOUR_SCRIPT.js`;
// Run a script after the build command netlifyConfig.build.command = netlifyConfig.build.command ? `${netlifyConfig.build.command} && ${newCommand}` : newCommand;
// Modify the build command’s environment variables netlifyConfig.build.environment.DATABASE_URI = getDatabaseUri();
// Add redirects netlifyConfig.redirects.push({ from: "/ORIGIN_PATH", to: "/DESTINATION_PATH", });
// Add headers netlifyConfig.headers.push({ for: "/YOUR_PATH", values: { YOUR_HEADER_NAME: "YOUR_HEADER_VALUE" }, });
// Add edge functions netlifyConfig.edge_functions ? netlifyConfig.edge_functions.push({ path: "/YOUR_PATH", function: "YOUR_EDGE_FUNCTION" }) : (netlifyConfig.edge_functions = [{ path: "/YOUR_PATH", function: "YOUR_EDGE_FUNCTION" }]);});
packageJson
Each event handler includes a packageJson
argument. When an event handler executes, the contents of the package.json
in a site’s base directory get passed to the build event handler. The data fields are normalized to prevent build event handler errors. If the site has no package.json
, the argument is an empty object.
To access the packageJson
object in your build event handler code, use the following pattern:
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
extension.addBuildEventHandler("onPreBuild", ({ packageJson }) => { console.log(packageJson);});
Environment variables
Build event handlers can access build environment variables two different ways:
process.env
: includes all Netlify build environment variables and any variables a user declares using the Netlify UI ornetlify.toml
. We recommend you use this when you only need to get values during the build process.netlifyConfig.build.environment
: includes only the variables a user declares using the Netlify UI ornetlify.toml
. We recommend you use this when you need to modify values during the build process.
Visit our Forums for a verified Support Guide on how to access environment variables during a site’s build.
Build event handler methods
We’ve provided a number of utilities and API methods to assist you in writing your build event handler.
Utilities
Several utilities are provided with the utils
argument to event handlers:
build
: used to report errors or cancel buildsstatus
: used to display information in the deploy summarycache
: used to cache files between buildsrun
: used to run commands and processesgit
: used to retrieve Git-related information such as the list of modified, created, or deleted files
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
extension.addBuildEventHandler("onPreBuild", async ({ utils: { build, status, cache, run, git } }) => { await run.command("eslint src/ test/");});
Error reporting
Exceptions thrown inside event handlers are reported in logs as bugs. Instead of using the onError
event to handle exceptions, build event handlers should rely on try
/catch
/finally
blocks and use utils.build
:
The following methods are available depending on the error’s type:
utils.build.failBuild("YOUR_MESSAGE")
: method that fails the build. The build in the user’s dashboard would show “Failed”. Use this to indicate something went wrong.utils.build.failPlugin("YOUR_MESSAGE")
: method that fails the build event handler but not the build.utils.build.cancelBuild("YOUR_MESSAGE")
: method that cancels the build. The user’s dashboard would show “Canceled” for that build. Use this to indicate that the build is being canceled as planned.
utils.build.failBuild()
, utils.build.failPlugin()
and utils.build.cancelBuild()
can specify an options object with
the following properties:
error
: originalError
instance. Its stack trace will be preserved and its error message will be appended to the"YOUR_MESSAGE"
argument.
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
extension.addBuildEventHandler("onPreBuild", ({ utils }) => { try { badMethod(); } catch (error) { utils.build.failBuild("YOUR_MESSAGE", { error }); }});
Logging
Anything logged to the console is printed in the build logs.
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
extension.addBuildEventHandler("onPreBuild", () => { console.log("This is printed in the build logs");});
If you’d prefer to make the information more prominent for users, use utils.status.show()
to display information in the deploy
summary instead.
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
extension.addBuildEventHandler("onPreBuild", ({ utils }) => { utils.status.show({ // Optional. Default to the extension’s name followed by a generic title. title: "Main title", // Required. summary: "Message below the title", // Optional. Empty by default. text: "Detailed information shown in a collapsible section", });});
Only one status is shown per build event handler. Calling utils.status.show()
twice overrides the previous status.
This is meant for successful information. Errors should be reported with utils.build.*
instead.
Asynchronous code
Asynchronous code can be achieved by using async
methods:
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
extension.addBuildEventHandler("onPreBuild", async ({ utils }) => { try { await doSomethingAsync(); } catch (error) { utils.build.failBuild("YOUR_MESSAGE", { error }); }});
Any thrown Error
or rejected Promise
that is not handled by utils.build
will be shown in the build logs as a build event handler bug.
import { NetlifyExtension } from "@netlify/sdk";
const extension = new NetlifyExtension();
extension.addBuildEventHandler("onPreBuild", async ({ utils }) => { // Any error thrown inside this function will be shown // in the build logs as a build event handler bug.
await doSomethingAsync();});
Build event handlers end as soon as their methods end. Therefore you should await
any asynchronous operation. The following
examples show invalid code and the way to fix it.
// Example of how to use callbacks.import { NetlifyExtension } from "@netlify/sdk";
const { promisify } = require("util");
const extension = new NetlifyExtension();
// VALID EXAMPLE: please use this.// This callback will be awaited.extension.addBuildEventHandler("onPostBuild", async ({ utils }) => { const response = await promisify(doSomethingAsync)();
console.log(response);});
// INVALID EXAMPLE: do not use this.// This callback will not be awaited.extension.addBuildEventHandler("onPostBuild", ({ utils }) => { doSomethingAsync((error, response) => { console.log(response); });});
// Example of how to use events.import { NetlifyExtension } from "@netlify/sdk";import pEvent from "p-event";
const extension = new NetlifyExtension();
// VALID EXAMPLE: please use this.// This event will be awaited.extension.addBuildEventHandler("onPreBuild", async ({ utils }) => { const emitter = doSomethingAsync();
emitter.start();
const response = await pEvent(emitter, "response");
console.log(response);});
// INVALID EXAMPLE: do not use this.// This event will not be awaited.extension.addBuildEventHandler("onPreBuild", ({ utils }) => { const emitter = doSomethingAsync();
emitter.on("response", (response) => { console.log(response); });
emitter.start();});
// Example of how to use `Array.map()`.
const extension = new NetlifyExtension();
// VALID EXAMPLE: please use this.// This callback will be awaited.extension.addBuildEventHandler("onPostBuild", async ({ utils }) => { await Promise.all( array.map(async () => { await doSomethingAsync(); }) );});
// INVALID EXAMPLE: do not use this.// This callback will not be awaited.extension.addBuildEventHandler("onPreBuild", ({ utils }) => { array.forEach(async () => { await doSomethingAsync(); });});