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, Nuxt.js, Angular, or any other framework that supports server side rendering.
- Alter request and response headers
- Send synthetic responses
- Configure multiple destinations for A/B testing
Prerequisites
Before proceeding, you will need an Edgio property. Create one now if you do not already have one.
Configuration
Define routes within the
routes.[js|ts]
file. This file should export an instance of @edgio/core/router/Router
:JavaScript./routes.js
1const { Router } = require('@edgio/core/router')23module.exports = 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.
JavaScript./routes.js
1const { Router } = require('@edgio/core/router')23module.exports = new Router().get('/some-path', ({ cache, proxy }) => {4 // handle the request here5})
All HTTP methods are available:
- get
- put
- post
- patch
- delete
- head
To match all methods, use
match
:JavaScript./routes.js
1const { Router } = require('@edgio/core/router')23module.exports = new Router().match('/some-path', ({ cache, proxy }) => {4 // handle the request here5})
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. The following methods return 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
1const { Router } = require('@edgio/core/router')2const { nextRoutes } = require('@edgio/next')34// In this example a request to /products/1 will be cached by the first route, then served by the `nextRoutes` middleware5new Router()6 .get('/products/:id', ({ cache }) => {7 cache({8 edge: { maxAgeSeconds: 60 * 60, staleWhileRevalidateSeconds: 60 * 60 },9 })10 })11 .use(nextRoutes)
Alter Requests and Responses
Edgio offers APIs to manipulate request and response headers and cookies. The APIs are:
Operation | Request | Upstream Response | Response sent to Browser |
---|---|---|---|
Set header | setRequestHeader | setUpstreamResponseHeader | setResponseHeader |
Add cookie | * | addUpstreamResponseCookie | addResponseCookie |
Update header | updateRequestHeader | updateUpstreamResponseHeader | updateResponseHeader |
Update cookie | * | updateUpstreamResponseCookie | updateResponseCookie |
Remove header | removeRequestHeader | removeUpstreamResponseHeader | removeResponseHeader |
Remove cookie | * | removeUpstreamResponseCookie | removeResponseCookie |
*
Adding, updating, or removing a request cookie can be achieved with updateRequestHeader
applied to cookie
header.You can find detailed descriptions of these APIs in the
@edgio/core
documentation.Embedded Values
You can inject values from the request or response into headers or cookies as template literals using the
${value}
format. For example: setResponseHeader('original-request-path', '${path}')
would add an original-request-path
response header whose value is the request path.Value | Embedded value | Description |
---|---|---|
HTTP method | ${method} | The value of the HTTP method used for the request (e.g. GET ) |
URL | ${url} | The complete URL path including any query strings (e.g. /search?query=docs ). Protocol, hostname, and port are not included. |
Path | ${path} | The URL path excluding any query strings (e.g. /search ) |
Query string | ${query:<name>} | The value of the <name> query string or empty if not available. |
Request header | ${req:<name>} | The value of the <name> request header or empty if not available. |
Request cookie | ${req:cookie:<name>} | The value of the <name> cookie in cookie request header or empty if not available. |
Request named parameter | ${req:param:<name>} | The value of the <name> param defined in the route or empty if not available. |
Response header | ${res:<name>} | The value of the <name> response header or empty if not available. |
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
1new Router().get('/:foo/:bar', res => {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
1new Router().get('/icon-:foo(\\d+).png', res => {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
1new Router().get('/:attr1?{-:attr2}?{-:attr3}?', res => {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
1new Router().get('/:foo/(.*)', res => {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
1new Router().get('/:foo/:bar?', res => {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
1new Router().get('/:foo*', res => {2 /* res.params.foo will be an array */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
1new Router().get('/:foo+', res => {2 /* res.params.foo will be an array */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 syntax4 method: /GET|POST/i, // value is a regular expression5 cookies: { currency: /^(usd)$/i }, // keys are cookie names, values are regular expressions6 headers: { 'x-moov-device': /^desktop$/i }, // keys are header names, values are regular expressions7 query: { page: /^(1|2|3)$/ }, // keys are query parameter names, values are regular expressions8 },9 () => {},10)
Request Handling
The second argument to routes is a function that receives a
ResponseWriter
and uses it to send a response. Using ResponseWriter
you can:- Proxy a backend configured in
edgio.config.js
- Serve a static file
- Send a redirect
- Send a synthetic response
- Cache the response at edge and in the browser
- Manipulate request and response headers
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.The search engine traffic is automatically blocked on Edgio edge links and permalinks as of Edgio v6.
If you would like to enable indexing on those links, you need to pass
{ indexPermalink: true }
into the Router constructor in routes.js
file:JavaScript
1new Router({ indexPermalink: true })
Otherwise, Edgio will match requests with the
host
header matching /layer0.link|layer0-perma.link/
and set a response header of x-robots-tag: noindex, nofollow
.Additionally, you can customize this to block traffic to development or staging websites based on the
host
header of the request:JavaScript
1router2 .get(3 {4 headers: {5 // Regex to catch multiple hostnames6 host: /dev.example.com|staging.example.com/,7 },8 },9 ({ setResponseHeader }) => {10 setResponseHeader('x-robots-tag', 'noindex, nofollow')11 },12 )
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
1const { Router } = require('@edgio/core/router')23module.exports = new Router()4 .get('/service-worker.js', ({ serviceWorker }) => {5 // serve the service worker built by webpack6 serviceWorker('dist/service-worker.js')7 })8 .get('/p/:productId', ({ cache }) => {9 // cache products for one hour at edge and using the service worker10 cache({11 edge: {12 maxAgeSeconds: 60 * 60,13 staleWhileRevalidateSeconds: 60 * 60,14 },15 browser: {16 maxAgeSeconds: 0,17 serviceWorkerSeconds: 60 * 60,18 },19 })20 proxy('origin')21 })22 .fallback(({ proxy }) => {23 // serve all unmatched URLs from the origin backend configured in edgio.config.js24 proxy('origin')25 })
Errors Handling
You can use the router’s
catch
method to return specific content when the request results in an error status (For example, a status code of 537). Using catch
, you can also alter the statusCode
and response
on the edge before issuing a response to the user.JavaScript
1router.catch(RegExp | string | number, (routeHandler: Function))
Examples
For example, to issue a custom error page when the origin returns any 5xx status code:
JavaScriptroutes.js
1const { Router } = require('@edgio/core/router')23module.exports = new Router()4 // Example route that returns with a 5xx error status code5 .get('/failing-route', ({ proxy }) => {6 proxy('broken-origin')7 })8 // So let's assume that the route above returns 5xx, so instead of rendering9 // the broken-origin response we can alter that by specifing .catch10 .catch(/5[0-9][0-9]/, ({ serveStatic }) => {11 // The file below is present at the root of the directory12 serveStatic('customized-error-page.html', { statusCode: 502 })13 })
The
.catch
method allows the edge router to render a response based on the result preceeding routes. So in the example above whenever we receive a 5xx, we respond with customized-error-page.html
from the application’s root directory, and change the status code to 502.- Your catch callback is provided a ResponseWriter instance. You can use any ResponseWriter method except
proxy
inside.catch
. - We highly recommend keeping
catch
routes simple. Serve responses usingserveStatic
instead ofsend
to minimize the size of the edge bundle.
Environment Edge Redirects
In addition to sending redirects at the edge within the router configuration, this can also be configured at the environment level within the Edgio Developer console.
Under <Your Environment> → Configuration, click Edit to draft a new configuration. Scroll down to the Redirects section:
Click Add A Redirect to configure the path or host you wish to redirect to:
Note: you will need to activate and redeploy your site for this change to take effect.