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.
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 * @async5 * @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.Parameter | Type | Description | Reference |
---|---|---|---|
request | Object | Represents the incoming request | Request |
context | Object | A read-only object providing additional information about the request and your environment | |
context.client | Key-value store | The client’s network information | virt_ variables in Feature Variables |
context.device | Key-value store | The client’s device capabilities | wurfl_ variables in Feature Variables |
context.environmentVars | Key-value store | Environment variables as defined in the Edgio Console (Property -> Environment —> Environment Variables) | Environment Variables |
context.geo | Key-value store | The client’s geo location | geo_ variables in Feature Variables |
context.metrics | Object | Provides functions for injecting metrics into your edge function | Access Logs |
context.origins | Key-value store | Origin servers as defined in the Edgio Console (Property -> Environment -> Origins) or the edgio.config.js file | Origin Configuration |
context.requestVars | Key-value store | Information about this property including values set using Set Variables | Set Variables |
context.respondWith(response) | Function |
| context.respondWith(response) |
context.waitUntil(promise) | Function | Waits until the given promise is fulfilled | context.waitUntil(promise) |
Metrics Functions
Function | Description |
---|---|
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/Class | Description | Reference |
---|---|---|
console object | The standard console object used to log messages to the console. | Console Object |
Headers Class | The standard Headers class used to manipulate headers on requests and responses. | Headers Class |
Request Class | The standard Request class used access the initial request on this route and to make new requests to the origin server. | Request Class |
Response Class | The standard Response class used to access responses from the origin server and to create new downstream responses | Response Class |
fetch(request) | A modified fetch() function used to makes requests to the origin server. | Fetch API |
TextDecoder | Polyfill class to manage decoding text. | TextDecoder |
TextEncoder | Polyfill 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()
- ArrayBuffer:
- Method:
request.method
to get the HTTP method of the request. - URL:
request.url
provides the full URL, andrequest.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()
- ArrayBuffer:
-
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 */);45 if (!response.ok) {6 return defaultResponse;7 }89 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 request4 edgio: {5 origin: 'web',6 },7 });89 // handle the response as needed10 /* ... */1112 // return the response to the client13 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');34 const resp = await fetch('https://your-server.com');56 // handle the response as needed7 /* ... */89 // return the response to the client10 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';34const fetch = createFetchForOrigin('planetscale');56export async function handleHttpRequest(request, context) {7 const env = context.environmentVars;89 const config = {10 host: env.PLANETSCALE_HOST,11 username: env.PLANETSCALE_USERNAME,12 password: env.PLANETSCALE_PASSWORD,13 fetch,14 };1516 const conn = connect(config);1718 // make a request to the PlanetScale API19 /* ... */2021 // return the response to the client22 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';23export async function handleHttpRequest(request, context) {4 const fetch = createFetchForOrigin('api');56 // override the global fetch() function7 global.fetch = fetch;89 // make a request to the your API10 /* ... */1112 // return the response to the client13 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.
- From the command line or terminal, type
edgio dev
. - 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: 41730922023-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 theURL
APIJavaScript./polyfills/url-parse.js1// npm install url-parse2import URL from 'url-parse';34global.URL = URL; -
whatwg-url
for theURL
andURLSearchParams
APIsJavaScript./polyfills/whatwg-url.js1// npm install whatwg-url2import {URL, URLSearchParams} from 'whatwg-url';34global.URL = URL;5global.URLSearchParams = URLSearchParams; -
Buffer
APIJavaScript./polyfills/buffer.js1global.Buffer = require('buffer').Buffer;23global.btoa = function (str) {4 return Buffer.from(str, 'binary').toString('base64');5};67global.atob = function (b64Encoded) {8 return Buffer.from(b64Encoded, 'base64').toString('binary');9}; -
process.env
NamespaceThis 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.js1/**2 * Polyfill for process.env.3 */4global.process = global.process || {env: {}};56/**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
-
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 afetch()
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.js1/**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() function9 * 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 }2122 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';45const fetch = createFetchForOrigin('web');67export 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';23const fetch = createFetchForOrigin('legacy_server');45// Example edge function that injects a header into the response6export async function handleHttpRequest(request, context) {7 // Forward the incoming request to the defined origin server.8 const response = await fetch(request);910 // Add a header to the response from the origin server.11 response.headers.set('X-Edge-Function', 'home-page.js');1213 // Return the response and end the edge function.14 return resp;15}
JavaScript./edge-functions/product.js
1import createFetchForOrigin from './polyfills/createFetchForOrigin';23const fetch = createFetchForOrigin('json_api_server');45// Example edge function that modifies a response from the origin server6export async function handleHttpRequest(request, context) {7 // Forward the incoming request to the defined origin server.8 const response = await fetch(request);910 // Parse the response body as JSON11 const body = await response.json();1213 // Add the customer's postal_code to the json response14 body.postal_code = context.geo.postal_code;1516 // 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);2021 return new Response(jsonBody, response);22}
JavaScript./edge-functions/contacts.js
1import createFetchForOrigin from './polyfills/createFetchForOrigin';23const fetch = createFetchForOrigin('json_api_server');45// Example edge function makes multiple fetches6export async function handleHttpRequest(request, context) {7 const myBackend = 'http://api.backend-example.com';89 // Get the list of phone contacts10 const phonePromise = fetch(new Request(`${myBackend}/phone`));1112 // In Parallel, get the list of e-mail contacts13 const emailPromise = fetch(new Request(`${myBackend}/email`));1415 // Wait for both requests to complete.16 const [phoneResponse, emailResponse] = await Promise.all([17 phonePromise,18 emailPromise,19 ]);2021 // Combine the two response bodies into a single response22 const body = {23 phone: await phoneResponse.json(),24 email: await emailResponse.json(),25 };2627 // Return the response and end the edge function as JSON28 const jsonBody = JSON.stringify(body);2930 return new Response(jsonBody, 200, {31 headers: {'Content-Type': 'application/json'},32 });33}