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

CDN-as-Code (EdgeJS)

Upgrading from Edgio Applications version 6 or earlier may require significant changes to your CDN-as-code configuration as certain core legacy components have limited support. View our upgrading guide.
The @edgio/core package provides a JavaScript API for controlling routing and caching from your code base rather than a CDN web portal. Using this EdgeJS approach allows this vital routing logic to be properly tested, reviewed, and version controlled, just like the rest of your application code.
Using the Router, you can:
  • Proxy requests to upstream sites
  • Send redirects from the network edge
  • Render responses on the server using Next.js and Nuxt.js
  • Alter request and response headers
  • Send synthetic responses

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.

Configuration

Define routes within the routes.[js|ts] file. This file should export an instance of @edgio/core/router/Router:
JavaScript./routes.js
1import {Router} from '@edgio/core';
2
3export default new Router();
By default, our CLI automatically creates routes.js and edgio.config.js upon initializing a property (edgio init). If your web application supports TypeScript and it uses a framework for which we have a TypeScript implementation, then our CLI will create routes.ts instead of routes.js.

Declare Routes

Declare routes using the method corresponding to the HTTP method you want to match. All HTTP methods are available:
  • get
  • put
  • post
  • patch
  • delete
  • head
To match all methods, use match:
JavaScript./routes.js
1router.match('/:path*', {});

Route Pattern Syntax

The syntax for route paths is provided by path-to-regexp, which is the same library used by Express.

Named Parameters

Named parameters are defined by prefixing a colon to the parameter name (:foo).
JavaScript
1router.get('/:foo/:bar', {
2 /* ... */
3});
Please note: Parameter names must use “word characters” ([A-Za-z0-9_]).

Custom Matching Parameters

Parameters can have a custom regexp, which overrides the default match ([^/]+). For example, you can match digits or names in a path:
JavaScript
1router.get('/icon-:foo(\\d+).png', {
2 /* ... */
3});
Tip: Backslashes need to be escaped with another backslash in JavaScript strings.

Custom Prefix and Suffix

Parameters can be wrapped in {} to create custom prefixes or suffixes for your segment:
JavaScript
1router.get('/:attr1?{-:attr2}?{-:attr3}?', {
2 /* ... */
3});

Unnamed Parameters

It is possible to write an unnamed parameter that only consists of a regexp. It works the same the named parameter, except it will be numerically indexed:
JavaScript
1router.get('/:foo/(.*)', {
2 /* ... */
3});

Modifiers

Modifiers must be placed after the parameter (e.g. /:foo?, /(test)?, /:foo(test)?, or {-:foo(test)}?).

Optional

Parameters can be suffixed with a question mark (?) to make the parameter optional.
JavaScript
1router.get('/:foo/:bar?', {
2 /* ... */
3});
Tip: The prefix is also optional, escape the prefix \/ to make it required.

Zero or More

Parameters can be suffixed with an asterisk (*) to denote zero or more parameter matches.
JavaScript
1router.get('/:foo*', {
2 /* ... */
3});
The captured parameter value will be provided as an array.

One or More

Parameters can be suffixed with a plus sign (+) to denote one or more parameter matches.
JavaScript
1router.get('/:foo+', {
2 /*... */
3});
The captured parameter value will be provided as an array.

Matching Method, Query Parameters, Cookies, and Headers

Match can either take a URL path, or an object which allows you to match based on method, query parameters, cookies, or request headers:
JavaScript
1router.match(
2 {
3 path: '/some-path', // value is route-pattern syntax
4 method: /GET|POST/i, // value is a regular expression
5 cookies: {currency: /^(usd)$/i}, // keys are cookie names, values are regular expressions
6 headers: {'x-moov-device': /^desktop$/i}, // keys are header names, values are regular expressions
7 query: {page: /^(1|2|3)$/}, // keys are query parameter names, values are regular expressions
8 },
9 {
10 /* ... */
11 }
12);

Negated Route Matching

Previously, we showed how to match requests based on path, method, query parameters, cookies, and request headers. You can also negate these matches by specifying a not key in the object passed to your route criteria. For example, the following route matches all requests whose relative path does not match /some-path:
JavaScript
1router.match(
2 {
3 path: {
4 not: '/some-path',
5 },
6 },
7 {
8 caching: {
9 max_age: '1d',
10 },
11 }
12);
Similarly, you can negate matches based on method, query parameters, cookies, and request headers:
JavaScript
1router.match(
2 {
3 path: '/some-path',
4 query: {
5 page: {
6 not: /^(1|2|3)$/,
7 },
8 },
9 method: {
10 not: /POST/i,
11 },
12 cookies: {
13 currency: {
14 not: /^(usd)$/i,
15 },
16 },
17 headers: {
18 'x-device': {
19 not: /^desktop$/i,
20 },
21 },
22 },
23 {
24 caching: {
25 max_age: '1d',
26 },
27 }
28);
This example matches all requests to /some-path except for those with query parameter page=1|2|3

Exact Path, Inclusive, and Regular Expression Matching

As described in Route Pattern Syntax, this type of route matching is based on path-to-regexp. While this is a rather universal approach to matching requests, Edgio provides additional options for matching requests.

Exact Path Matching

Exact path matching, also known as strict matching, gives you precise control over how requests are matched. Traditionally, you may match /some-path with the following route:
JavaScript
1router.match('/some-path', {
2 /* ... */
3});
This will match /some-path, /Some-Path, and other variations in between that are case-insensitive. However, using exact will use strict comparison in matching the request path. The following example shows how to import the exact function and use it to match requests to /some-path:
JavaScript
1import {Router, exact} from '@edgio/core';
2
3const router = new Router();
4
5router.match(exact('/some-path'), {
6 /* ... */
7});
8
9export default router;
This matches the path literally, so /some-path will match, but /Some-Path will not.

Inclusive Matching

Inclusive matching uses the InOperatorValues type for matching a generic array of values. To use this, you must specify the argument as a RouteCriteria type for the path you would like to match against. This type of matching is similar to exact matching in that it uses strict comparison.
For example, the following route matches requests to /some-path and /another-path, but not /Some-Path or /Another-Path:
JavaScript
1router.match(
2 {
3 path: ['/some-path', '/another-path'],
4 },
5 {
6 /* ... */
7 }
8);

Regular Expression Matching

For complex routes that cannot be easily matched using path-to-regexp, you can use regular expressions to match requests. For example, the following route matches requests to /some-path and /another-path, but not /Some-Path or /Another-Path:
JavaScript
1router.match(
2 {
3 path: /^(\/some-path|\/another-path)$/i,
4 },
5 {
6 /* ... */
7 }
8);
You may also use Negated Route Matching with regular expressions:
JavaScript
1router.match(
2 {
3 path: {
4 not: /^(\/some-path|\/another-path)$/i,
5 },
6 },
7 {
8 /* ... */
9 }
10);
Regular expression matching is also available for matching query parameters, cookies, and request headers, and more. Any property of RouteCriteria that accepts CriteriaValue or OptionalCriteriaValue types can use a regular expression and negation.

Request Handling

The second argument to routes is a function that receives a Features type and uses it to send a response, such as:
  • Proxy a backend configured in edgio.config.js
  • Serve a static file
  • Send a redirect
  • Cache the response at edge and in the browser
  • Manipulate request and response headers
For example, to cache a response for requests to /hello-world:
JavaScript
1router.get('/hello-world', {
2 caching: {
3 max_age: '1d',
4 },
5});

Route Execution

When Edgio receives a request, it executes each route that matches the request in the order in which they are declared until one sends a response.
Multiple routes can therefore be executed for a given request. A common pattern is to add caching with one route and render the response with a later one using middleware. In the following example we cache then render a response with Next.js:
JavaScript
1import {Router} from '@edgio/core';
2import {nextRoutes} from '@edgio/next';
3
4// In this example a request to /products/1 will be cached by the first route, then served by the `nextRoutes` middleware
5export default new Router()
6 .get('/products/:id', {
7 caching: {max_age: {200: '1h'}, stale_while_revalidate: '1h'},
8 })
9 .use(nextRoutes);

Alter Requests and Responses

Edgio offers APIs to manipulate request and response headers and cookies. The APIs are:
OperationRequestResponse sent to Browser
Add headerset_request_headersadd_response_headers
Add cookie**
Update headerset_request_headersset_response_headers
Update cookie**
Remove headerset_request_headersremove_response_headers
remove_origin_response_headers
Remove cookie**
* Adding, updating, or removing request cookies can be achieved with set_request_headers applied to cookie header. Similarly, adding, updating, or removing response cookies can be achieved with set_response_headers applied to set-cookie header.

Request / Response Variables

You can inject values into the request or response via cache key rewrite, headers, cookies, URL redirect and rewrite as template literals using the %{<FEATURE VALUE>} format.
Feature VariableDescription
%{arg_<QUERY STRING PARAMETER>}Returns the value corresponding to the query string parameter identified by the <QUERY STRING PARAMETER> term.
%{cookie_<COOKIE>}Returns the value corresponding to the cookie identified by the <COOKIE> term.
%{host}Indicates the host defined in the request URL.
%{http_<REQUEST HEADER>}Returns the value corresponding to the request header identified by the <REQUEST HEADER> term.
%{normalized_path}Indicates the normalized relative path for the request submitted to the CDN.
%{normalized_query}Indicates the normalized query string defined in the request URL.
%{normalized_uri}Indicates the normalized relative path and query string for the request submitted to the CDN.
%{path}Indicates the relative path to the requested content. This relative path reflects URL rewrites due to url_rewrite.
%{query_string}Indicates the entire query string value defined in the request URL.
%{referring_domain}Indicates the domain defined in the Referer request header.
%{request}Describes the request.
%{request_method}Indicates the HTTP request method.
%{request_protocol}Indicates the request protocol used by an edge server to proxy the request.
%{request_uri}Indicates the relative path, including the query string, defined in the request URI.
%{resp_<RESPONSE HEADER>}Returns the value corresponding to the response header identified by the <RESPONSE HEADER> term.
%{status}Indicates the HTTP status code for the response.

Example

This example shows how you would add an original-request-path response header for all requests whose value is the request path:
JavaScript
1router.match(
2 {},
3 {
4 headers: {
5 set_response_header: {
6 'original-request-path': '%{path}',
7 },
8 },
9 }
10);
For a comprehensive list of variables, see the Feature Variables guide.

Blocking Search Engine Crawlers

If you need to block all search engine bot traffic to specific environments (such as your default or staging environment), the easiest way is to include the x-robots-tag header with the same directives you would otherwise set in a meta tag.
Additionally, you can customize this to block traffic to development or staging websites based on the host header of the request:
JavaScript
1router.get(
2 {
3 headers: {
4 // Regex to catch multiple hostnames
5 host: /dev.example.com|staging.example.com/,
6 },
7 },
8 {
9 headers: {
10 set_response_headers: {
11 'x-robots-tag': 'noindex, nofollow',
12 },
13 },
14 }
15);

Full Example

This example shows typical usage of @edgio/core, including serving a service worker, Next.js routes (vanity and conventional routes), and falling back to a legacy backend.
JavaScript🗄️ routes.js
1import { Router } from "@edgio/core";
2
3export default new Router()
4 .get("/:path*", {
5 caching: { max_age: "30758400s", bypass_client_cache: true },
6 url: {
7 url_rewrite: [
8 {
9 source: "/service-worker.js",
10 syntax: "path-to-regexp",
11 destination: "/dist/service-worker.js",
12 },
13 ],
14 },
15 headers: { set_request_headers: { "x-edg-serverless-hint": "" } },
16 origin: { set_origin: "edgio_static" },
17 })
18 .get("/:path*", {
19 caching: {
20 max_age: "3600s",
21 stale_while_revalidate: "3600s",
22 service_worker_max_age: 3600,
23 bypass_client_cache: true,
24 },
25 headers: { set_response_headers: { "x-sw-cache-control": "max-age=3600" } },
26 origin: { set_origin: "origin" },
27 })
28 .match("/:path*", { origin: { set_origin: "origin" } });