Introducing Edgio Applications v7Find out what's new.
Edgio
Edgio

Edge Functions

Edge Functions enable you to execute a small piece of JavaScript code on our edge servers. This code can be used to modify requests and responses as well as make additional calls to your defined origins. The benefits of using Edge Functions include enhancing performance by reducing latency, improving user experience by personalizing content, and increasing security by managing authentication and redirection at the edge. They allow you to execute your code closer to users, reducing the need to go back to the original server and thus, provide faster services.
Key information:
  • This article assumes that you are familiar with our CDN-as-Code approach for defining rules.
  • Edge Functions requires activation. Contact your account manager or our sales department at 1 (866) 200 - 5463 to upgrade your account.

Prerequisites

Before proceeding, you will need an Edgio property. Create one now if you do not already have one.
Learn how to create a property.

System Requirements

Sign up for Edgio

Deploying requires an account on Edgio. Sign up here for free.

Install the Edgio CLI

If you have not already done so, install the Edgio CLI.
Bash
1npm i -g @edgio/cli@latest

Defining Edge Functions

Define an edge function within your routes.[js|ts] file by adding the edge_function property to a route. The edge_function property accepts a string representing the path to the edge function file.
JavaScript./routes.js
1new Router().get('/', {
2 edge_function: './path/to/function.js',
3});
Each edge function is stored in a separate file and assigned to a specific route in your routes.js file. An edge function file must export the following entry point:
JavaScript
1/**
2 * Handles an HTTP request and returns a response.
3 *
4 * @async
5 * @param {Request} request - Represents the incoming request.
6 * @param {Object} context - Provides additional information about the request and environment.
7 * @param {Object} context.client - The client's network information.
8 * @param {Object} context.device - The client's device capabilities.
9 * @param {Object} context.environmentVars - Environment variables as defined in the Developer Console.
10 * @param {Object} context.geo - The client's geo location.
11 * @param {Object} context.metrics - Provides functions for injecting metrics into your edge function.
12 * @param {Function} context.metrics.add - Adds a value to the metric with the given ID.
13 * @param {Function} context.metrics.startTimer - Starts a timer for the metric with the given ID. Only one timer can be active at a time for a given metric ID.
14 * @param {Function} context.metrics.stopTimer - Stops a timer for the metric with the given ID.
15 * @param {Object} context.origins - Origin servers as defined in the Edgio Console or the edgio.config.js file.
16 * @param {Object} context.requestVars - Information about this property including values set using Set Variables.
17 * @returns {Response | Promise<Response>}
18 */
19export async function handleHttpRequest(request, context) {
20 // ... function code ...
21}
When a request is received for a route that has an edge function assigned to it, the edge function is invoked.

Edge Function Parameters

The edge function is passed two parameters: request and context.
request is an object representing the incoming request and context is a read-only object providing additional information about the request and your environment, such as access to the client’s network information, device capabilities, geo location, environment variables, origin servers, and information about this property including values set using variables. It also provides functions for injecting metrics into your edge function and for returning the response from your edge function to the downstream client.
ParameterTypeDescriptionReference
requestObjectRepresents the incoming requestRequest
contextObjectA read-only object providing additional information about the request and your environment
context.clientKey-value storeThe client’s network informationvirt_ variables in Feature Variables
context.deviceKey-value storeThe client’s device capabilitieswurfl_ variables in Feature Variables
context.environmentVarsKey-value storeEnvironment variables as defined in the Edgio Console (Property -> Environment —> Environment Variables)Environment Variables
context.geoKey-value storeThe client’s geo locationgeo_ variables in Feature Variables
context.metricsObjectProvides functions for injecting metrics into your edge functionAccess Logs
context.originsKey-value storeOrigin servers as defined in the Edgio Console (Property -> Environment -> Origins) or the edgio.config.js fileOrigin Configuration
context.requestVarsKey-value storeInformation about this property including values set using Set VariablesSet Variables
context.respondWith(response)Function
  • Edgio v7.2.3 or higher: Deprecated. See Responding to the Client.
  • Edgio v7.2.2 or lower: Must be called to return the response from your edge function to the downstream client.
context.respondWith(response)
context.waitUntil(promise)FunctionWaits until the given promise is fulfilledcontext.waitUntil(promise)

Metrics Functions

FunctionDescription
context.metrics.add(id: integer, value: integer)Adds a value to the metric with the given id
context.metrics.startTimer(id: integer)Starts a timer for the metric with the given id. Only one timer can be active at a time for a given metric id
context.metrics.stopTimer(id: integer)Stops a timer for the metric with the given id

Edge Function Namespace

Edge Functions global namespace provide access to the following:
Global Object/ClassDescriptionReference
console objectThe standard console object used to log messages to the console.Console Object
Headers ClassThe standard Headers class used to manipulate headers on requests and responses.Headers Class
Request ClassThe standard Request class used access the initial request on this route and to make new requests to the origin server.Request Class
Response ClassThe standard Response class used to access responses from the origin server and to create new downstream responsesResponse Class
fetch(request)A modified fetch() function used to makes requests to the origin server.Fetch API
TextDecoderPolyfill class to manage decoding text.TextDecoder
TextEncoderPolyfill class to manage encoding text.TextEncoder

Request Class

Edge functions use a modified version of the standard Request API. See the Unsupported Methods and Properties section for more information.
Edge functions are passed a Request instance representing the incoming request. This object provides methods and properties for accessing the request’s headers, body, URL, and more.

Supported Methods and Properties

  • Headers: Access the request headers using request.headers.
  • Body: The request body can be read as:
    • ArrayBuffer: await request.arrayBuffer()
    • JSON: await request.json()
    • Text: await request.text()
  • Method: request.method to get the HTTP method of the request.
  • URL: request.url provides the full URL, and request.path gives the request path.
  • Cloning: To clone a request without its body, use request.cloneWithoutBody().

Unsupported Methods and Properties

The following properties and methods from the standard Request API are not supported:
  • request.blob()
  • request.cache
  • request.credentials
  • request.clone()
  • request.destination
  • request.formData()
  • request.integrity
  • request.mode
  • request.redirect
  • request.referrer
  • request.referrerPolicy
  • request.signal
Using an unsupported method or property will throw an error.

Response Class

Edge functions use a modified version of the standard Response API. See the Unsupported Methods and Properties section for more information.
Origin fetch requests and edge functions return a Response instance representing the response. This object provides methods and properties for accessing and setting the response’s headers, body, status code, and more. Create a response through the Response class or by calling the fetch() function. See the Edge Function Namespace section for more information.

Supported Methods and Properties

  • Headers: Access or modify the response headers using response.headers.
  • Body: The response body can be interacted with using:
    • ArrayBuffer: await response.arrayBuffer()
    • JSON: await response.json()
    • Text: await response.text()
  • Status: response.status to get the HTTP status code of the response. response.statusText provides the corresponding status text.
  • URL: response.url provides the URL of the response.
  • Redirected: response.redirected is a property that indicates whether the response is a result of a redirection.
    You may redirect a response up to 5 times before an exception is thrown.
  • Redirection: Create a redirected response using Response.redirect(url, status).
  • Cloning: To clone a response without its body, use response.cloneWithoutBody().

Unsupported Methods and Properties

The following properties and methods from the standard Response API are not supported:
  • response.blob()
  • response.clone()
  • response.formData()
  • response.type
Note: The above-mentioned unsupported methods and properties will throw an error if attempted to be used.

Responding to the Client

Edge functions must respond to the client by returning a Response object or a Promise that resolves to a Response object. The Response object can be created using the Response class or by calling the fetch() function. See the Edge Function Namespace section for more information.
JavaScript./edge-functions/example.js
1export async function handleHttpRequest(request, context) {
2 const defaultResponse = new Response('Hello World!');
3 const response = await fetch('https://your-server.com' /* origin options */);
4
5 if (!response.ok) {
6 return defaultResponse;
7 }
8
9 return response;
10}
As of v7.2.3, the context.respondWith() function is deprecated. You must return a Response object or a Promise that resolves to a Response object to respond to the client.

Origin Requests Using fetch()

Before making a fetch, you must first define the origin in the edgio.config.js file. Learn more in our CDN-as-Code guide.
To fetch a resource from an origin server using the fetch() function, specify the URL or a Request object as the first argument. The second argument is also required and is where you specify the name of the origin, plus any additional options compatible with the fetch() function.
JavaScript
1export async function handleHttpRequest(request, context) {
2 const resp = await fetch('https://your-server.com', {
3 // an Edgio origin must be specified in the request
4 edgio: {
5 origin: 'web',
6 },
7 });
8
9 // handle the response as needed
10 /* ... */
11
12 // return the response to the client
13 return resp;
14}
You can also use the createFetchForOrigin() function to create a modified fetch() function that includes the origin server. See the Polyfills section for more information.
JavaScript
1export async function handleHttpRequest(request, context) {
2 const fetch = createFetchForOrigin('web');
3
4 const resp = await fetch('https://your-server.com');
5
6 // handle the response as needed
7 /* ... */
8
9 // return the response to the client
10 return resp;
11}
Some libraries allow you to specify a fetch() function to use. For example, PlanetScale’s database driver configuration accepts a custom function to use when making requests to the API.
JavaScript
1import {connect} from '@planetscale/database';
2import {createFetchForOrigin} from './polyfills';
3
4const fetch = createFetchForOrigin('planetscale');
5
6export async function handleHttpRequest(request, context) {
7 const env = context.environmentVars;
8
9 const config = {
10 host: env.PLANETSCALE_HOST,
11 username: env.PLANETSCALE_USERNAME,
12 password: env.PLANETSCALE_PASSWORD,
13 fetch,
14 };
15
16 const conn = connect(config);
17
18 // make a request to the PlanetScale API
19 /* ... */
20
21 // return the response to the client
22 return resp;
23}
This approach allows for creating unique fetch() functions for each origin server. Optionally, you can override the global fetch() function if you are unable to specify a fetch() function in your library.
JavaScript
1import createFetchForOrigin from './polyfills';
2
3export async function handleHttpRequest(request, context) {
4 const fetch = createFetchForOrigin('api');
5
6 // override the global fetch() function
7 global.fetch = fetch;
8
9 // make a request to the your API
10 /* ... */
11
12 // return the response to the client
13 return resp;
14}

Testing Locally

You may run Edgio in local development mode to preview your website on your local machine prior to deployment. Local development mode allows for rapid development by letting you to quickly test changes prior to deployment.
  1. From the command line or terminal, type edgio dev.
  2. Preview your website by loading https://127.0.0.1:3000 from within your preferred web browser.
Note that edge functions executed in local development mode are simulated and may not reflect the behavior or performance of deployed edge functions.

Deploying Your Property

Evaluate site performance and QA functionality by deploying your property to Edgio. Run the following command from your property’s root directory:
Bash
1edgio deploy
Note that Edge Functions must be enabled for your Edgio Console team in order to deploy your property. Contact support to enable this feature.

Limitations

Edge functions are limited to 2MB of memory at runtime. This includes the compiled JavaScript byte code, variables, requests, context object, and responses. All functions are compiled into a single bundle to deploy to our edge servers. If the total size of all compiled functions exceeds 2MB, the deployment will fail with a 400 error.
plaintext
12023-08-14T17:37:04Z - error - external - Schema validation error: properties.0.edge_functions.quickjs_bytecode_base64 exceeds maximum size. Max: 204800, got: 417309
22023-08-14T17:37:04Z - error - external - Error: the server responded with status 400
Edge functions are limited to 50ms of CPU time and 60 seconds of total execution time. The time your edge function spends waiting for a response from an origin server does not count against the 50ms CPU limit.

Polyfills and Helpers

It’s important to note that edge functions are not Node.js functions. Your code or third-party libraries may not work as expected if they are referencing Node.js specific APIs (e.g. URL, Buffer, etc). Because of this, we recommend using polyfills when needed. Below are some examples of polyfills you can use in your edge functions. Add these files to your project and import them into your edge function files as needed.
  • url-parse for the URL API
    JavaScript./polyfills/url-parse.js
    1// npm install url-parse
    2import URL from 'url-parse';
    3
    4global.URL = URL;
  • whatwg-url for the URL and URLSearchParams APIs
    JavaScript./polyfills/whatwg-url.js
    1// npm install whatwg-url
    2import {URL, URLSearchParams} from 'whatwg-url';
    3
    4global.URL = URL;
    5global.URLSearchParams = URLSearchParams;
  • Buffer API
    JavaScript./polyfills/buffer.js
    1global.Buffer = require('buffer').Buffer;
    2
    3global.btoa = function (str) {
    4 return Buffer.from(str, 'binary').toString('base64');
    5};
    6
    7global.atob = function (b64Encoded) {
    8 return Buffer.from(b64Encoded, 'base64').toString('binary');
    9};
  • process.env Namespace
    This namespace is not globally available in edge functions. You can use the following polyfill to set environment variables from the context object.
    JavaScript./polyfills/process-env.js
    1/**
    2 * Polyfill for process.env.
    3 */
    4global.process = global.process || {env: {}};
    5
    6/**
    7 * Sets environment variables from a given context.
    8 *
    9 * @param {Object} context - The context object containing environment variables.
    10 * @param {Object} context.environmentVars - Key-value pairs of environment variables.
    11 */
    12export function setEnvFromContext({environmentVars}) {
    13 Object.assign(process.env, environmentVars);
    14}

Helper Functions

  • createFetchForOrigin for the fetch() API
    This function returns a modified fetch() function that includes the origin server. This is useful for making multiple requests to the same origin or overriding the global function.
    Some third-party libraries let you specify a fetch() function. If you are unable to set this in your library, you can override the global one using this helper. See the Origin Requests Using fetch() section for more details.
    JavaScript./polyfills/createFetchForOrigin.js
    1/**
    2 * Creates a fetch function with an additional 'edgio' option to specify the origin. Example usage:
    3 *
    4 * // create a fetch function for the 'web' origin.
    5 * const fetch = createFetchForOrigin('web');
    6 * const response = await fetch('https://your-server.com');
    7 *
    8 * // override the global fetch() function
    9 * global.fetch = createFetchForOrigin('web');;
    10 *
    11 * @param {string} originName - The origin name defined in edgio.config.js.
    12 * @returns {function} - A modified fetch function.
    13 * @throws {Error} If the origin name is not provided.
    14 */
    15export default function createFetchForOrigin(originName) {
    16 if (!originName) {
    17 throw new Error(
    18 "'originName' is required and must be a name defined in edgio.config.js"
    19 );
    20 }
    21
    22 return (url, options = {}) => {
    23 const modifiedOptions = {
    24 ...options,
    25 edgio: {
    26 origin: originName,
    27 },
    28 };
    29 return fetch(url, modifiedOptions);
    30 };
    31}

Usage

You can import these polyfills into your edge function file and use them as needed. For example, if you need to use the URL and Buffer APIs, you can import the files directly.
JavaScript./edge-functions/example.js
1import './polyfills/url-parse.js';
2import './polyfills/buffer.js';
3import createFetchForOrigin from './polyfills/createFetchForOrigin';
4
5const fetch = createFetchForOrigin('web');
6
7export async function handleHttpRequest(request, context) {
8 const response = await fetch(/* ... */);
9 /* ... */
10}

Polyfill Limitations

It’s worth noting that not all implementations will be able to accept polyfills, either due to the number of dependencies affected or the compiled size of the polyfill exceeding the Limitations of Edge Functions.

Edge Function Examples

Below are some examples of how to use Edge Functions by Edgio. We also maintain a GitHub repository with more detailed examples of real-world use cases.
JavaScript./routes.js
1// Example router assigning edge functions to different routes.
2new Router()
3 .get('/', {
4 edge_function: './edge-functions/home-page.js',
5 })
6 .get('/products:id', {
7 edge_function: './edge-functions/product.js',
8 })
9 .get('/contacts', {
10 edge_function: './edge-functions/contacts.js',
11 });
JavaScript./edge-functions/home-page.js
1import createFetchForOrigin from './polyfills/createFetchForOrigin';
2
3const fetch = createFetchForOrigin('legacy_server');
4
5// Example edge function that injects a header into the response
6export async function handleHttpRequest(request, context) {
7 // Forward the incoming request to the defined origin server.
8 const response = await fetch(request);
9
10 // Add a header to the response from the origin server.
11 response.headers.set('X-Edge-Function', 'home-page.js');
12
13 // Return the response and end the edge function.
14 return resp;
15}
JavaScript./edge-functions/product.js
1import createFetchForOrigin from './polyfills/createFetchForOrigin';
2
3const fetch = createFetchForOrigin('json_api_server');
4
5// Example edge function that modifies a response from the origin server
6export async function handleHttpRequest(request, context) {
7 // Forward the incoming request to the defined origin server.
8 const response = await fetch(request);
9
10 // Parse the response body as JSON
11 const body = await response.json();
12
13 // Add the customer's postal_code to the json response
14 body.postal_code = context.geo.postal_code;
15
16 // Return the response and end the edge function.
17 // Note: Since the original response body is read-only,
18 // we must create a new response with the updated body.
19 const jsonBody = JSON.stringify(body);
20
21 return new Response(jsonBody, response);
22}
JavaScript./edge-functions/contacts.js
1import createFetchForOrigin from './polyfills/createFetchForOrigin';
2
3const fetch = createFetchForOrigin('json_api_server');
4
5// Example edge function makes multiple fetches
6export async function handleHttpRequest(request, context) {
7 const myBackend = 'http://api.backend-example.com';
8
9 // Get the list of phone contacts
10 const phonePromise = fetch(new Request(`${myBackend}/phone`));
11
12 // In Parallel, get the list of e-mail contacts
13 const emailPromise = fetch(new Request(`${myBackend}/email`));
14
15 // Wait for both requests to complete.
16 const [phoneResponse, emailResponse] = await Promise.all([
17 phonePromise,
18 emailPromise,
19 ]);
20
21 // Combine the two response bodies into a single response
22 const body = {
23 phone: await phoneResponse.json(),
24 email: await emailResponse.json(),
25 };
26
27 // Return the response and end the edge function as JSON
28 const jsonBody = JSON.stringify(body);
29
30 return new Response(jsonBody, 200, {
31 headers: {'Content-Type': 'application/json'},
32 });
33}