Simulating an S3 static website on localhost with Yulin
If you’re using S3 to host a static website and want to test and develop it locally, you can use the @kensio/yulin package.
This might seem pointless when it’s easy to serve a static website on localhost in various other
ways (such as with hugo serve), but even for this small use-case Yulin does provide some extra
benefits.
One immediate small benefit is that Yulin simulates behaviours of S3 static website serving such as error documents, bucket redirects and routing rules with conditions. These might be important behaviours for your project, so it’s useful to be able to develop locally with simulated equivalents.
Yulin also serves the simulated S3 website on a realistic hostname like
foobar.s3-website.eu-west-2.sim-aws.localhost. This means you can simulate multiple S3 websites
together at the same time, each with its own hostname and realistic paths from the root. The AWS
region is also included in the simulated AWS behaviours.
One further benefit of using Yulin for local development is that you can share the same configuration between local development serving and your automated tests. In other words, configure Yulin once and then use it for both local development and automated testing.
Here’s an example of configuring a simulated S3 static website in Yulin v0.10.0:
#!/usr/bin/env -S pnpm tsx
import { SimAws } from "@kensio/yulin";
import { installSimS3 } from "@kensio/yulin/s3";
import { serveSimAws } from "@kensio/yulin/serve";
import {
CreateBucketCommand,
PutBucketWebsiteCommand,
} from "@aws-sdk/client-s3";
const simAws = new SimAws();
installSimS3(simAws);
const srv = await serveSimAws(simAws);
const simS3 = simAws.region("eu-west-2").service("s3");
await simS3.createBucket(new CreateBucketCommand({ Bucket: "foobar-website" }));
await simS3.putBucketWebsite(
new PutBucketWebsiteCommand({
Bucket: "foobar-website",
WebsiteConfiguration: {
IndexDocument: {
Suffix: "index.html",
},
},
}),
);
simS3.mountBucketFilesystem("foobar-website", "/path/to/static/website/public");
const bucketWebsiteUrl = srv.localUrl(
simS3.getBucketWebsiteUrl("foobar-website"),
);
console.log(bucketWebsiteUrl.toString());
Yulin v0.10.0 is still a beta version, so the exact API might change a bit in future releases.
Note that Yulin uses the real command classes from the AWS JS SDK. Also note that each simulated AWS
service is installed with an installer function like installSimS3(). This is so that projects only
need to install the specific AWS SDK packages they need, such as @aws-sdk/client-s3.
The tsx shebang line at the top makes that an executable script. When you run it, Yulin sets up the simulation according to the configuration provided. The bucket website URL at the end will look like:
http://foobar-website.s3-website.eu-west-2.sim-aws.localhost:58436/
Note the automatic selection of an available port. This is the default behaviour if no port is specified. It’s useful to avoid clashing with other processes that might already be listening on a default port.
You can then open the simulated S3 website URL in your browser and interact with it as normal.
You could also share the same Yulin configuration across this local dev script and your automated tests. It’s easy to run in CI as well, as only package installation is required. There are no container images to download or networking configuration to manage.