Router
Reference doc for the `sst.aws.Router` component.
The Router
component lets you use a CloudFront distribution to direct
requests to various parts of your application.
How it works
Behind the scenes, this uses:
- The default CloudFront distribution behavior
- A CloudFront function for routing
- And a KV store to store the routing data
When a request comes in, it does a lookup in the KV store and dynamically sets the origin based on the routing data.
You might notices a placeholder.sst.dev behavior in CloudFront. This is not used and is only there because CloudFront requires a behavior to exist.
Minimal example
const router = new sst.aws.Router("MyRouter");router.route("/*", "https://some-external-service.com");
Add a custom domain
new sst.aws.Router("MyRouter", { domain: "myapp.com"});
Route to a function URL
const myFunction = new sst.aws.Function("MyFunction", { handler: "src/api.handler", url: true,});
const router = new sst.aws.Router("MyRouter", {});router.route("/*", myFunction.url);
Route to a bucket
const myBucket = new sst.aws.Bucket("MyBucket", { access: "cloudfront"});
const router = new sst.aws.Router("MyRouter");router.routeBucket("/files/*", myBucket);
Make sure to allow CloudFront access to the bucket by setting the access
prop on the bucket.
Route to a frontend
const site = new sst.aws.Nextjs("Site", { cdn: false });
const router = new sst.aws.Router("MyRouter");router.routeSite("/*", site);
We are disabling the built-in CDN of the Next.js app because we want to use the router to serve the app.
Route to a frontend on a subpath
To serve your frontend from a subpath, you need to:
-
Configure the base path in your frontend framework.
Set the
basePath
in yournext.config.js
.next.config.js export default defineConfig({basePath: "/docs"});Set the
base
option in yourastro.config.mjs
.astro.config.mjs export default defineConfig({adapter: sst(),base: "/docs"});Set the
base
option in yourvite.config.ts
with the trailing slash.vite.config.ts export default defineConfig({plugins: [...],base: "/docs/"});Set the
base
option in yoursvelte.config.js
without the trailing slash.svelte.config.js export default defineConfig({kit: {paths: {base: "/docs"}}});Set the
base
option in yourvite.config.ts
with the trailing slash.vite.config.ts export default defineConfig({plugins: [tailwindcss(), reactRouter(), tsconfigPaths()],base: "/docs/"});Set the
baseURL
option in yournuxt.config.ts
without a trailing slash.nuxt.config.ts export default defineConfig({app: {baseURL: "/docs"}});Set the
baseURL
option in yourapp.config.ts
without a trailing slash.app.config.ts export default defineConfig({server: {baseURL: "/docs"}});Set the
base
andapiPrefix
options in yourvite.config.ts
. TheapiPrefix
value should not begin with a slash.vite.config.ts export default defineConfig(({ mode }) => ({plugins: [analog({apiPrefix: "docs/api"})],base: "/docs"})); -
Disable the built-in CDN of the frontend.
sst.config.ts const site = new sst.aws.Nextjs("Site", { cdn: false });sst.config.ts const site = new sst.aws.Astro("Site", { cdn: false });sst.config.ts const site = new sst.aws.Remix("Site", { cdn: false });sst.config.ts const site = new sst.aws.SvelteKit("Site", { cdn: false });sst.config.ts const site = new sst.aws.React("Site", { cdn: false });sst.config.ts const site = new sst.aws.Nuxt("Site", { cdn: false });sst.config.ts const site = new sst.aws.SolidStart("Site", { cdn: false });sst.config.ts const site = new sst.aws.Analog("Site", { cdn: false }); -
Add the site to a Router.
sst.config.ts const router = new sst.aws.Router("MyRouter");router.routeSite("/docs", site);
Constructor
new Router(name, args, opts?)
Parameters
-
name
string
-
args
RouterArgs
-
opts?
ComponentResourceOptions
RouterArgs
domain?
Type Input
<
string
|
Object
>
Set a custom domain for your Router.
Automatically manages domains hosted on AWS Route 53, Cloudflare, and Vercel. For other
providers, you’ll need to pass in a cert
that validates domain ownership and add the
DNS records.
By default this assumes the domain is hosted on Route 53.
{ domain: "example.com"}
For domains hosted on Cloudflare.
{ domain: { name: "example.com", dns: sst.cloudflare.dns() }}
Specify a www.
version of the custom domain.
{ domain: { name: "domain.com", redirects: ["www.domain.com"] }}
domain.aliases?
Type Input
<
string
[]
>
Alias domains that should be used. Unlike the redirect
option, this keeps your visitors
on this alias domain.
So if your users visit app2.domain.com
, they will stay on app2.domain.com
in their
browser.
{ domain: { name: "app1.domain.com", aliases: ["app2.domain.com"] }}
domain.cert?
Type Input
<
string
>
The ARN of an ACM (AWS Certificate Manager) certificate that proves ownership of the domain. By default, a certificate is created and validated automatically.
The certificate will be created in the us-east-1
region as required by AWS CloudFront.
If you are creating your own certificate, you must also create it in us-east-1
.
To manually set up a domain on an unsupported provider, you’ll need to:
- Validate that you own the domain by creating an ACM certificate. You can either validate it by setting a DNS record or by verifying an email sent to the domain owner.
- Once validated, set the certificate ARN as the
cert
and setdns
tofalse
. - Add the DNS records in your provider to point to the CloudFront distribution URL.
{ domain: { name: "domain.com", dns: false, cert: "arn:aws:acm:us-east-1:112233445566:certificate/3a958790-8878-4cdc-a396-06d95064cf63" }}
domain.dns?
Type Input
<
false
|
sst.aws.dns
|
sst.cloudflare.dns
|
sst.vercel.dns
>
Default sst.aws.dns
The DNS provider to use for the domain. Defaults to the AWS.
Takes an adapter that can create the DNS records on the provider. This can automate validating the domain and setting up the DNS routing.
Supports Route 53, Cloudflare, and Vercel adapters. For other providers, you’ll need
to set dns
to false
and pass in a certificate validating ownership via cert
.
Specify the hosted zone ID for the Route 53 domain.
{ domain: { name: "example.com", dns: sst.aws.dns({ zone: "Z2FDTNDATAQYW2" }) }}
Use a domain hosted on Cloudflare, needs the Cloudflare provider.
{ domain: { name: "example.com", dns: sst.cloudflare.dns() }}
Use a domain hosted on Vercel, needs the Vercel provider.
{ domain: { name: "example.com", dns: sst.vercel.dns() }}
domain.name
Type Input
<
string
>
The custom domain you want to use.
{ domain: { name: "example.com" }}
Can also include subdomains based on the current stage.
{ domain: { name: `${$app.stage}.example.com` }}
domain.redirects?
Type Input
<
string
[]
>
Alternate domains to be used. Visitors to the alternate domains will be redirected to the
main name
.
Use this to create a www.
version of your domain and redirect visitors to the apex domain.
{ domain: { name: "domain.com", redirects: ["www.domain.com"] }}
edge?
Type Object
-
viewerRequest?
Input
<
Object
>
-
viewerResponse?
Input
<
Object
>
Configure CloudFront Functions to customize the behavior of HTTP requests and responses at the edge.
edge.viewerRequest?
Type Input
<
Object
>
Configure the viewer request function.
The viewer request function can be used to modify incoming requests before they reach your origin server. For example, you can redirect users, rewrite URLs, or add headers.
edge.viewerRequest.injection
Type Input
<
string
>
The code to inject into the viewer request function.
By default, a viewer request function is created to:
- Disable CloudFront default URL if custom domain is set.
- Add the
x-forwarded-host
header. - Route requests to the corresponding target based on the domain and request path.
The given code will be injected at the beginning of this function.
async function handler(event) { // User injected code
// Default behavior code
return event.request;}
To add a custom header to all requests.
{ edge: { viewerRequest: { injection: `event.request.headers["x-foo"] = "bar";` } }}
edge.viewerRequest.kvStore?
Type Input
<
string
>
The KV store to associate with the viewer request function.
{ edge: { viewerRequest: { kvStore: "arn:aws:cloudfront::123456789012:key-value-store/my-store" } }}
edge.viewerResponse?
Type Input
<
Object
>
Configure the viewer response function.
The viewer response function can be used to modify outgoing responses before they are sent to the client. For example, you can add security headers or change the response status code.
By default, no viewer response function is set. A new function will be created with the provided code.
edge.viewerResponse.injection
Type Input
<
string
>
The code to inject into the viewer response function.
async function handler(event) { // User injected code
return event.response;}
To add a custom header to all responses.
{ edge: { viewerResponse: { injection: `event.response.headers["x-foo"] = "bar";` } }}
edge.viewerResponse.kvStore?
Type Input
<
string
>
The KV store to associate with the viewer response function.
{ edge: { viewerResponse: { kvStore: "arn:aws:cloudfront::123456789012:key-value-store/my-store" } }}
invalidation?
Type Input
<
boolean
|
Object
>
Default Invalidation is turned off
Configure how the CloudFront cache invalidations are handled.
Enable invalidations. Setting this to true
will invalidate all paths. It is equivalent
to passing in { paths: ["/*"] }
.
{ invalidation: true}
invalidation.paths?
Type Input
<
Input
<
string
>
[]
>
Default [”/*”]
Specify an array of glob pattern of paths to invalidate.
Invalidate the index.html
and all files under the products/
route. This counts as two invalidations.
{ invalidation: { paths: ["/index.html", "/products/*"] }}
invalidation.token?
Type Input
<
string
>
Default A unique value is auto-generated on each deploy
The invalidation token is used to determine if the cache should be invalidated. If the token is the same as the previous deployment, the cache will not be invalidated.
{ invalidation: { token: "foo123" }}
invalidation.wait?
Type Input
<
boolean
>
Default false
Configure if sst deploy
should wait for the CloudFront cache invalidation to finish.
Waiting for this process to finish ensures that new content will be available after the deploy finishes. However, this process can sometimes take more than 5 mins.
{ invalidation: { wait: true }}
transform?
Type Object
Transform how this component creates its underlying resources.
transform.cachePolicy?
Type CachePolicyArgs
|
(
args
:
CachePolicyArgs
,
opts
:
ComponentResourceOptions
,
name
:
string
)
=>
void
Transform the Cache Policy that’s attached to each CloudFront behavior.
transform.cdn?
Type CdnArgs
|
(
args
:
CdnArgs
,
opts
:
ComponentResourceOptions
,
name
:
string
)
=>
void
Transform the CloudFront CDN resource.
Properties
distributionID
Type Output
<
string
>
The ID of the Router distribution.
nodes
nodes.cdn
Type Output
<
Cdn
>
The Amazon CloudFront CDN resource.
url
Type Output
<
string
>
The URL of the Router.
If the domain
is set, this is the URL with the custom domain.
Otherwise, it’s the autogenerated CloudFront URL.
SDK
Use the SDK in your runtime to interact with your infrastructure.
Links
This is accessible through the Resource
object in the SDK.
-
url
string
The URL of the Router.
If the
domain
is set, this is the URL with the custom domain. Otherwise, it’s the autogenerated CloudFront URL.
Methods
route
route(pattern, url, args?)
Parameters
The path pattern to match for this route.pattern
Input
<
string
>
The destination URL to route matching requests to.url
Input
<
string
>
Configure the route.args?
Input
<
RouterUrlRouteArgs
>
Returns void
Add a route to a destination URL.
You can match a route based on:
- A path like
/api
- A domain pattern like
api.example.com
- A combined pattern like
dev.example.com/api
For example, to match a path.
router.route("/api", "https://api.example.com");
Or match a domain.
router.route("api.myapp.com/", "https://api.example.com");
Or a combined pattern.
router.route("dev.myapp.com/api", "https://api.example.com");
You can also rewrite the request path.
router.route("/api", "https://api.example.com", { rewrite: { regex: "^/api/(.*)$", to: "/$1" }});
Here something like /api/users/profile
will be routed to
https://api.example.com/users/profile
.
routeBucket
routeBucket(pattern, bucket, args?)
Parameters
The path pattern to match for this route.pattern
Input
<
string
>
The S3 bucket to route matching requests to.bucket
Input
<
Bucket
>
Configure the route.args?
Input
<
RouterBucketRouteArgs
>
Returns void
Add a route to an S3 bucket.
Let’s say you have an S3 bucket that gives CloudFront access
.
const bucket = new sst.aws.Bucket("MyBucket", { access: "cloudfront"});
You can match a pattern and route to it based on:
- A path like
/api
- A domain pattern like
api.example.com
- A combined pattern like
dev.example.com/api
For example, to match a path.
router.routeBucket("/files", bucket);
Or match a domain.
router.routeBucket("files.example.com", bucket);
Or a combined pattern.
router.routeBucket("dev.example.com/files", bucket);
You can also rewrite the request path.
router.routeBucket("/files", bucket, { rewrite: { regex: "^/files/(.*)$", to: "/$1" }});
Here something like /files/logo.png
will be routed to
/logo.png
.
routeSite
routeSite(pattern, site)
Parameters
The path pattern to match for this route.pattern
Input
<
string
>
The frontend or static site to route matching requests to.site
Input
<
All SSR sites
|
StaticSite
>
Returns void
Add a route to a frontend or static site.
You can add routes to a frontend (like Next.js, Remix, SvelteKit) or a static site.
Let’s say you have a Next.js app with its built-in CDN disabled.
const site = new sst.aws.Nextjs("Site", { cdn: false });
You can then match a pattern and route to it based on:
- A path like
/docs
- A domain pattern like
docs.example.com
- A combined pattern like
dev.example.com/docs
For example, to match a path.
router.routeSite("/docs", site);
Or match a domain.
router.routeSite("docs.example.com/", site);
Route by both domain and path:
router.routeSite("dev.example.com/docs", site);
If you are routing to a path like /docs
, you must configure the
base path in your frontend’s config. The base path must match the path in your
route pattern.
For example, if you are routing /docs
to a Next.js app, you need to set
basePath
to /docs
in your next.config.js
.
export default defineConfig({ basePath: "/docs"});
static get
Router.get(name, distributionID, opts?)
Parameters
The name of the component.name
string
The ID of the existing Router distribution.distributionID
Input
<
string
>
-
opts?
ComponentResourceOptions
Returns Router
Reference an existing Router with the given Router distribution ID.
Let’s say you create a Router in the dev
stage. And in your personal stage
frank
, you want to share the same Router.
const router = $app.stage === "frank" ? sst.aws.Router.get("MyRouter", "E2IDLMESRN6V62") : new sst.aws.Router("MyRouter");
Here E2IDLMESRN6V62
is the ID of the Router distribution created in the
dev
stage. You can find this by outputting the distribution ID in the dev
stage.
return { router: router.distributionID};
RouterBucketRouteArgs
connectionAttempts?
Type Input
<
number
>
Default 3
The number of times that CloudFront attempts to connect to the origin. Must be between 1 and 3.
{ connectionAttempts: 1}
connectionTimeout?
Type Input
<
“
${number} second
”
|
“
${number} seconds
”
>
Default “10 seconds”
The number of seconds that CloudFront waits before timing out and closing the connection to the origin. Must be between 1 and 10 seconds.
{ connectionTimeout: "3 seconds"}
rewrite?
Type Input
<
Object
>
Rewrite the request path.
If the route path is /files/*
and a request comes in for /files/logo.png
,
the request path the destination sees is /files/logo.png
.
If you want to serve the file from the root of the bucket, you can rewrite
the request path to /logo.png
.
{ rewrite: { regex: "^/files/(.*)$", to: "/$1" }}
rewrite.regex
Type Input
<
string
>
The regex to match the request path.
rewrite.to
Type Input
<
string
>
The replacement for the matched path.
RouterUrlRouteArgs
connectionAttempts?
Type Input
<
number
>
Default 3
The number of times that CloudFront attempts to connect to the origin. Must be between 1 and 3.
{ connectionAttempts: 1}
connectionTimeout?
Type Input
<
“
${number} second
”
|
“
${number} seconds
”
>
Default “10 seconds”
The number of seconds that CloudFront waits before timing out and closing the connection to the origin. Must be between 1 and 10 seconds.
{ connectionTimeout: "3 seconds"}
keepAliveTimeout?
Type Input
<
“
${number} second
”
|
“
${number} seconds
”
>
Default “5 seconds”
The number of seconds that CloudFront should try to maintain the connection to the destination after receiving the last packet of the response. Must be between 1 and 60 seconds
{ keepAliveTimeout: "10 seconds"}
readTimeout?
Type Input
<
“
${number} second
”
|
“
${number} seconds
”
>
Default “30 seconds”
The number of seconds that CloudFront waits for a response after routing a request to the destination. Must be between 1 and 60 seconds.
When compared to the connectionTimeout
, this is the total time for the
request.
{ readTimeout: "60 seconds"}
rewrite?
Type Input
<
Object
>
Rewrite the request path.
If the route path is /api/*
and a request comes in for /api/users/profile
,
the request path the destination sees is /api/users/profile
.
If you want to serve the route from the root, you can rewrite the request
path to /users/profile
.
{ rewrite: { regex: "^/api/(.*)$", to: "/$1" }}
rewrite.regex
Type Input
<
string
>
The regex to match the request path.
rewrite.to
Type Input
<
string
>
The replacement for the matched path.