{"id":14962861,"url":"https://github.com/thunder-so/cdk-spa","last_synced_at":"2025-06-18T22:36:20.991Z","repository":{"id":226550865,"uuid":"769009375","full_name":"thunder-so/cdk-spa","owner":"thunder-so","description":"AWS CDK stack to deploy single page applications (SPA) on S3 and CloudFront. Supports React, Vue, Svelte via Vite, and Astro, Gatsby and Nextjs Static","archived":false,"fork":false,"pushed_at":"2025-06-10T22:56:34.000Z","size":1947,"stargazers_count":5,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-10T22:57:41.185Z","etag":null,"topics":["astro","aws","cdk","cloudfront","gatsby","nextjs","react","s3-website","single-page-applications","static-site-generation","svelte","typescript","vite","vue"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@thunderso/cdk-spa","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/thunder-so.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-03-08T06:42:41.000Z","updated_at":"2025-06-10T22:55:43.000Z","dependencies_parsed_at":"2024-03-16T17:20:53.608Z","dependency_job_id":"f60d27c7-fb75-4ac0-a3f8-6359a15141aa","html_url":"https://github.com/thunder-so/cdk-spa","commit_stats":{"total_commits":90,"total_committers":2,"mean_commits":45.0,"dds":"0.011111111111111072","last_synced_commit":"a635d585e3af60ec1a0966457375ee0e3a696f84"},"previous_names":["thunder-so/stacks","thunder-so/cdk-spa"],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/thunder-so/cdk-spa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thunder-so%2Fcdk-spa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thunder-so%2Fcdk-spa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thunder-so%2Fcdk-spa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thunder-so%2Fcdk-spa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thunder-so","download_url":"https://codeload.github.com/thunder-so/cdk-spa/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thunder-so%2Fcdk-spa/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260646094,"owners_count":23041660,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["astro","aws","cdk","cloudfront","gatsby","nextjs","react","s3-website","single-page-applications","static-site-generation","svelte","typescript","vite","vue"],"created_at":"2024-09-24T13:30:37.002Z","updated_at":"2025-06-18T22:36:15.980Z","avatar_url":"https://github.com/thunder-so.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# CDK-SPA\nInstall any client-only SPA (single page application) on AWS with automatic deployment.\n\n- Fast responses from [CloudFront](https://aws.amazon.com/cloudfront/)\n- Automatic upload of the build files for CSR and static assets to [S3](https://aws.amazon.com/s3/) with optimized caching rules\n- Automatic build and deploy with [CodeBuild](https://aws.amazon.com/codebuild/) and [CodePipeline](https://aws.amazon.com/codepipeline/) from [Github](https://github.com/) repository.\n- Publicly available by a custom domain (or subdomain) via [Route53](https://aws.amazon.com/route53/)\n\n\n## Prerequisites\n\nYou need an [AWS account](https://aws.amazon.com/premiumsupport/knowledge-center/create-and-activate-aws-account/) to create and deploy the required resources for the site on AWS.\n\nThis package uses the `npm` package manager and is an ES6+ Module.\n\n## Installation\n\nInstall the package and its required dependencies:\n\n```bash\nnpm i @thunderso/cdk-spa --save-dev\n```\n\nYour `package.json` must also contain `tsx` and this specific version of `aws-cdk-lib` :\n\n```bash\nnpm i tsx aws-cdk-lib@2.150.0 --save-dev\n```\n\n## Setup\n\n1. Login into the AWS console and note the `Account ID`. You will need it in the configuration step.\n\n2. Run the following command to automatically create the required CDK stack entrypoint at `stack/index.ts`. This file defines the config how the app will be deployed via CDK. You should adapt the file to the project's needs, especially the props `env.account` (setup step 1).\n\n```bash\nnpx cdk-spa-init \n```\n\n### Enable Automatic Deployments\n\n1. [Create a Github Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) for your Github account. This token must be kept secure.\n\n2. [Create a Secrets Manager secret](https://docs.aws.amazon.com/secretsmanager/latest/userguide/manage_create-basic-secret.html) as `plaintext` with the Personal Access Token you created earlier. Note the `ARN` of the secret. E.g. `arn:aws:secretsmanager:\u003cREGION_NAME\u003e:\u003cACCOUNT_ID\u003e:secret:\u003csecret-name\u003e`.\n\n3. Input the noted `ARN` to the `githubAccessTokenArn` field in your stack.\n\n### Manage Domain with Route53 (Optional)\n\n1. [Create a hosted zone in Route53](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/AboutHZWorkingWith.html) for the desired domain, if you don't have one yet.\n\n  This is required to create DNS records for the domain to make the app publicly available on that domain. On the hosted zone details you should see the `Hosted zone ID` of the hosted zone.\n\n2. [Request a public global certificate in the AWS Certificate Manager (ACM)](https://docs.aws.amazon.com/acm/latest/userguide/gs-acm-request-public.html) for the desired domain in `us-east-1` *(global)* and validate it, if you don't have one yet.\n\n  This is required to provide the app via HTTPS on the public internet. Take note of the displayed `ARN` for the certificate. \n\n\u003e [!IMPORTANT]\n\u003e The certificate must be issued in `us-east-1` *(global)* regardless of the region used for the app itself as it will be attached to the CloudFront distribution which works globally.\n\n\n## Configuration\n\nThe `SPAStack` construct can be configured via the following props:\n\n\u003ctable width=\"100%\"\u003e\n  \u003cthead align=left\u003e\n    \u003ctr\u003e\n      \u003cth width=150\u003e\n        name\n      \u003c/th\u003e\n      \u003cth width=70\u003e\n        type\n      \u003c/th\u003e\n      \u003cth\u003e\n        description\n      \u003c/th\u003e\n    \u003c/tr\u003e\n  \u003c/thead\u003e\n  \u003ctbody align=left valign=top\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eenv\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003c!-- \u003ccode\u003eaccount: string\u003c/code\u003e\n        \u003ccode\u003eregion: string\u003c/code\u003e --\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003cstrong\u003eRequired\u003c/strong\u003e. Your account ID and your preferred region.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eapplication\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003cstrong\u003eRequired\u003c/strong\u003e. A string identifier for the project the site is part of. An application might have multiple different services.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eservice\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003cstrong\u003eRequired\u003c/strong\u003e. A string identifier the site. This can be seen as the name of the site.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr id=\"constructor-option-environment\"\u003e\n      \u003cth\u003e\n        \u003ccode\u003eenvironment\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\u003cstrong\u003eRequired\u003c/strong\u003e. A string to identify the environment of the app. This enables us to deploy multiple different environments of the same app, e.g., prod, dev.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\u003ctd colspan=3\u003e\u003csmall\u003eSource, build and deploy:\u003c/small\u003e\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr id=\"constructor-option-deployment\"\u003e\n      \u003cth\u003e\n        \u003ccode\u003esourceProps\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\u003cstrong\u003eRequired\u003c/strong\u003e. Provide the Github repository details.\n        \u003cul\u003e\n          \u003c!-- \u003cli\u003e\u003ccode\u003eprovider: string;\u003c/code\u003e E.g. github\u003c/li\u003e --\u003e\n          \u003cli\u003e\u003ccode\u003eowner: string;\u003c/code\u003e\u003c/li\u003e\n          \u003cli\u003e\u003ccode\u003erepo: string;\u003c/code\u003e\u003c/li\u003e\n          \u003cli\u003e\u003ccode\u003ebranchOrRef: string;\u003c/code\u003e\u003c/li\u003e\n          \u003cli\u003e\u003ccode\u003erootdir: string;\u003c/code\u003e\u003c/li\u003e\n        \u003cul\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003egithubAccessTokenArn\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003c!-- \u003cstrong\u003eRequired\u003c/strong\u003e. Create a Github Personal Access Token, save the token as a parameter in AWS Parameter Store. Provide the ARN. Ensure it is \u003ccode\u003eSecureString\u003c/code\u003e. --\u003e\n        \u003cstrong\u003eRequired\u003c/strong\u003e. Create a Github Personal Access Token, save the token as a secret in AWS Secrets Manager. Provide the ARN. Ensure it is \u003ccode\u003eplaintext\u003c/code\u003e.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003c!-- \u003ctr\u003e\u003ctd colspan=3\u003e\u003c/td\u003e\u003c/tr\u003e --\u003e\n    \u003ctr id=\"constructor-option-buildstep\"\u003e\n      \u003cth\u003e\n        \u003ccode\u003ebuildSpecFilePath\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\u003cstrong\u003eOptional\u003c/strong\u003e. If you have a custom CodeBuild Buildspec file for your app, provide relative path to the file. E.g. \u003ccode\u003estack/buildspec.yml\u003c/code\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003ebuildProps\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003cstrong\u003eRequired\u003c/strong\u003e if \u003ccode\u003ebuildSpecFilePath\u003c/code\u003e is blank. \n        \u003cul\u003e\n          \u003cli\u003e\u003ccode\u003eruntime: string;\u003c/code\u003e\u003c/li\u003e\n          \u003cli\u003e\u003ccode\u003eruntime_version: string;\u003c/code\u003e\u003c/li\u003e\n          \u003cli\u003e\u003ccode\u003einstallcmd: string;\u003c/code\u003e\u003c/li\u003e\n          \u003cli\u003e\u003ccode\u003ebuildcmd: string;\u003c/code\u003e\u003c/li\u003e\n          \u003cli\u003e\u003ccode\u003eoutputdir: string;\u003c/code\u003e\u003c/li\u003e\n        \u003cul\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\u003ctd colspan=3\u003e\u003csmall\u003eDomain settings:\u003c/small\u003e\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr id=\"constructor-option-domain\"\u003e\n      \u003cth\u003e\n        \u003ccode\u003edomain\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003cstrong\u003eOptional\u003c/strong\u003e. The domain (without the protocol) at which the app shall be publicly available. A DNS record will be automatically created in Route53 for the domain. This also supports subdomains. Examples: \"example.com\", \"sub.example.com\"\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003ehostedZoneId\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003cstrong\u003eOptional\u003c/strong\u003e. Required when domain is provided. The id of the hosted zone to create a DNS record for the specified domain. \n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003cth\u003e\n        \u003ccode\u003eglobalCertificateArn\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\n        \u003cstrong\u003eOptional\u003c/strong\u003e. Required when domain is provided. The ARN of the certificate to use on CloudFront for the app to make it accessible via HTTPS. The certificate must be issued for the specified domain in us-east-1 (global) regardless of the region specified via 'env.region' as CloudFront only works globally.\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\u003ctd colspan=3\u003e\u003csmall\u003eEdge functions:\u003c/small\u003e\u003c/td\u003e\u003c/tr\u003e\n    \u003ctr id=\"constructor-option-functions\"\u003e\n      \u003cth\u003e\n        \u003ccode\u003eedgeFunctionFilePath\u003c/code\u003e\n      \u003c/th\u003e\n      \u003cth\u003e\n        \u003ccode\u003estring\u003c/code\u003e\n      \u003c/th\u003e\n      \u003ctd\u003e\u003cstrong\u003eOptional\u003c/strong\u003e. If you have a custom CloudFront Functions file for your app, provide relative path to the file. E.g. \u003ccode\u003estack/custom.js\u003c/code\u003e\n      \u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\n## Deployment\n\nAfter the installation and the setup you are already good to go to build the app and to deploy it to AWS with this package by following the steps below:\n\n### 1. Bootstrap CDK\n\nDeploying stacks with the AWS CDK requires dedicated Amazon S3 buckets and other containers to be available to AWS CloudFormation during deployment. Creating these is called bootstrapping and is only required once per account and region. To bootstrap, run the following command:\n\n```\ncdk bootstrap aws://ACCOUNT-NUMBER/REGION\n```\n\nSee https://docs.aws.amazon.com/cdk/v2/guide/getting_started.html for details.\n\n### 2. Build and Deploy\n\nBy running the following script, the CDK stack will be deployed to AWS.\n\n```bash\nnpx cdk deploy --require-approval never --all --app=\"npx tsx stack/index.ts\" \n```\n\n## Destroy the Stack\n\nIf you want to destroy the stack and all its resources (including storage, e.g., access logs), run the following script:\n\n```bash\nnpx cdk destroy --require-approval never --all --app=\"npx tsx stack/index.ts\" \n```\n\n## Reference: Created AWS Resources\n\nIn the following, you can find an overview of the AWS resources that will be created by this package for reference.\n\n### SPAStack\n\nThis stack is responsible for deploying static sites and dynamic SPA apps to AWS.\nThe following AWS resources will be created by this stack:\n\n- [CloudFront](https://aws.amazon.com/CloudFront/): A distribution to route incoming requests to the S3 bucket to serve the static assets for the app.\n\n- [S3](https://aws.amazon.com/s3/): A bucket to store the client files and static assets of the build with optimized cache settings.\n\n- [CodeBuild](https://aws.amazon.com/codebuild/): A build project to automatically build and package the static assets from the source code.\n\n- [CodePipeline](https://aws.amazon.com/codepipeline/): A deployment pipeline to automate the release process, integrating with CodeBuild, S3, and other AWS services.\n\n- [Route53](https://aws.amazon.com/route53/): Two DNS records (`A` for IPv4 and `AAAA` for IPv6) in the configured hosted zone to make the app available on the internet via the configured custom domain.\n\n\n## Manually setting up CDK\n\nYou can use `SPAStack` as a CDK construct within your CDK code to seamlessly integrate hosting. Here's an example of how to use it:\n\nCreate a `stack` directory in your project root and an empty file `index.ts` and fill in the props accordingly. \n\n\u003e Use different filenames such as `production.ts` and `testing.ts` for environments.\n\n```ts\nimport { App } from \"aws-cdk-lib\";\nimport { SPAStack, type SPAProps } from \"@thunderso/cdk-spa\";\n\nconst appStackProps: SPAProps = {\n  env: {\n    account: 'your-account-id',\n    region: 'us-east-1'\n  },\n  application: 'your-application-id',\n  service: 'your-service-id',\n  environment: 'production',\n\n  // Your Github repository url contains https://github.com/\u003cowner\u003e/\u003crepo\u003e\n  sourceProps: {\n    owner: 'your-github-username',\n    repo: 'your-repo-name',\n    branchOrRef: 'main',\n    rootdir: ''\n  },\n\n  // Build variables for CodeBuild\n  // https://docs.aws.amazon.com/codebuild/latest/userguide/available-runtimes.html\n  buildProps: {\n    runtime: 'nodejs',\n    runtime_version: 20,\n    installcmd: 'npm ci',\n    buildcmd: 'npm run build',\n    outputdir: 'dist/'\n  },\n  // Providing a buildspec.yml will override buildProps and sourceProps.rootdir\n  // buildSpecFilePath: 'stack/buildspec.yml',\n\n  // Custom CloudFront Functions\n  // edgeFunctionFilePath: 'stack/custom.js',\n\n  // Optional: Domain settings\n  // - create a hosted zone for your domain\n  // - issue a global tls certificate in us-east-1 \n  domain: 'sub.example.com',\n  hostedZoneId: 'Z1D633PJRANDOM',\n  globalCertificateArn: 'arn:aws:acm:us-east-1:123456789012:certificate/abcd1234-abcd-1234-abcd-1234abcd1234',\n\n  // all resources created in the stack will be tagged\n  // tags: {\n  //   key: 'value'\n  // },\n};\n\nnew SPAStack(new App(), `${appStackProps.application}-${appStackProps.service}-${appStackProps.environment}-stack`, appStackProps);\n```\n\nRun the following command to deploy stack.\n\n```bash\nnpx cdk deploy --require-approval never --all --app=\"npx tsx stack/index.ts\" \n```\n\n## Advanced\n\nCloudfront edge functions can be used for URL rewrite, among other use-cases.\n\n\u003e [!NOTE]\n\u003e Check out the resources from [aws-samples/amazon-cloudfront-functions](https://github.com/aws-samples/amazon-cloudfront-functions) for examples.\n\n\n## Useful commands\n* `npx cdk deploy`  deploy this stack to your default AWS account/region\n* `npx cdk diff`    compare deployed stack with current state\n* `npx cdk synth`   emits the synthesized CloudFormation template\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthunder-so%2Fcdk-spa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthunder-so%2Fcdk-spa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthunder-so%2Fcdk-spa/lists"}