--- title: TypeScript functions description: Learn how to create and use EAS Build functions in your custom build configurations. --- import { Terminal } from '~/ui/components/Snippet'; import { Step } from '~/ui/components/Step'; import ImageSpotlight from '~/components/plugins/ImageSpotlight'; import { BoxLink } from '~/ui/components/BoxLink'; import { GithubIcon } from '@expo/styleguide-icons'; > **Warning** This functionality is in preview, under active development, and likely to change. EAS Build functions are a great way to extend the functionality of custom builds. You can use them to create reusable steps, and to write your logic in JavaScript, TypeScript, or Bash (more information in [`command` in the config schema](./schema.mdx#functionsfunction_namecommand)). This guide provides a walkthrough of creating a function in TypeScript. ## Initialize an EAS Build function module The easiest way to create an EAS Build function is to use the `create-eas-build-function` CLI tool. By running the following command from the same directory as your **eas.json** file, you can create a new custom TypeScript function: This creates a new module called `myFunction` in the **.eas/build** directory. The module will contain a pre-generated module configuration and the **src** directory with the **index.ts** file containing the default TypeScript function template. ```ts .eas/build/myFunction/src/index.ts // This file was autogenerated by `create-eas-build-function` command. // Go to README.md to learn more about how to write your own custom build functions. import { BuildStepContext } from '@expo/steps'; // interface FunctionInputs { // // specify the type of the inputs value and whether they are required here // // example: name: BuildStepInput; // } // interface FunctionOutputs { // // specify the function outputs and whether they are required here // // example: name: BuildStepOutput; // } async function myFunction( ctx: BuildStepContext // { // inputs, // outputs, // env, // }: { // inputs: FunctionInputs; // outputs: FunctionOutputs; // env: BuildStepEnv; // } ): Promise { ctx.logger.info('Hello from my TypeScript function!'); } export default myFunction; ``` ## Compiling the function and connect it to the config file After generating the function, you will want to compile it and connect it to your custom build YAML configuration. ### Compile the function Functions must be compiled to a single JavaScript file that can be run without installing any Node Module dependencies. The default `build` script for generated functions uses the [ncc](https://github.com/vercel/ncc) to compile your function into a single file with all its dependencies. If you don't have the `ncc` installed globally on your machine, run `npm install -g @vercel/ncc` to install it. Next, run the build script in the **.eas/build/myFunction** directory: This command triggers the `build` script placed in the **package.json** file of your custom function module. ```json package.json { ... "scripts": { ... "build": "ncc build ./src/index.ts -o build/ --minify --no-cache --no-source-map-register" /* @end */ ... }, ... } ``` The `build` script generates **build/index.js**. This file must be uploaded to EAS Build as a part of your project archive, so that the builder can run your function. Ensure that the file is not excluded by a **.gitignore** file or **.easignore** file. ### Connect the function to the custom build config Let's assume that you have a **config.yml** file in the **.eas/build** directory. It contains the following content: ```yaml .eas/build/config.yml build: name: My example config steps: - eas/checkout - run: name: Install dependencies command: npm install - run: name: Finished command: echo Finished ``` To add your function to the config, you need to add the following lines to the **config.yml** file: ```yaml .eas/build/config.yml build: name: My example config steps: - eas/checkout - run: name: Install dependencies command: npm install - run: name: Finished command: echo Finished # @info # functions: my_function: name: My function path: ./myFunction # @end # ``` The `path` property should be a relative path from the config file to your function directory. In this case, it's just `./myFunction`. Now, add a call to the `my_function` function in the **config.yml** file: ```yaml .eas/build/config.yml build: name: My example config steps: - eas/checkout - run: name: Install dependencies command: npm install # @info # - my_function # @end # - run: name: Finished command: echo Finished functions: my_function: name: My function path: ./myFunction ``` ## Working on the function For a more advanced example, let's say you want to make a function calculate the sum of two numbers and print the result to the console, and then output that value from the function. To do this, modify the **config.yml** and **index.ts** files to make the function accept two inputs called `num1` and `num2` and return their sum as an output called `sum`. ```yaml .eas/build/config.yml build: name: My example config steps: - eas/checkout - run: name: Install dependencies command: npm install # @info # - my_function: inputs: num1: 1 num2: 2 id: sum_function # @end # - run: name: Print the sum inputs: sum: ${ steps.sum_function.sum } command: echo ${ inputs.sum } - run: name: Finished command: echo Finished functions: my_function: name: My function # @info # inputs: - name: num1 type: number - name: num2 type: number outputs: - name: sum # @end # path: ./myFunction ``` ```ts .eas/build/myFunction/src/index.ts // This file was autogenerated by `create-eas-build-function` command. // Go to README.md to learn more about how to write your own custom build functions. import { BuildStepContext, BuildStepInput, BuildStepInputValueTypeName, BuildStepOutput, } from '@expo/steps'; /* @info */ interface FunctionInputs { // first template argument is the type of the input value, second template argument is a boolean indicating if the input is required num1: BuildStepInput; num2: BuildStepInput; } /* @end */ /* @info */ interface FunctionOutputs { // template argument is a boolean indicating if the output is required sum: BuildStepOutput; } /* @end */ async function myFunction( ctx: BuildStepContext, { inputs, outputs, }: // env, { inputs: FunctionInputs; outputs: FunctionOutputs; // env: BuildStepEnv; } ): Promise { /* @info */ ctx.logger.info(`num1: ${inputs.num1.value}`); ctx.logger.info(`num2: ${inputs.num2.value}`); const sum = inputs.num1.value + inputs.num2.value; ctx.logger.info(`sum: ${sum}`); outputs.sum.set(sum.toString()); // Currently, outputs must be strings. This will improve in the future. /* @end */ } export default myFunction; ``` > **Info** Remember to compile your function each time you make changes to it: `npm run build`. ## Summary - Writing functions is a great way to extend the functionality of custom builds with your own logic. - EAS Build functions are reusable — you can use them in multiple custom build configurations. - Using EAS Build functions is a great option for more advanced use cases that are not easy to do by writing shell scripts. - Most of the [built-in functions](/custom-builds/schema/#built-in-eas-functions) are open-source and can be forked or used as a reference for writing your own functions. Check out the **example repository** for more detailed examples: