Skip to content

Instantly share code, notes, and snippets.

@antstanley
Last active January 16, 2025 14:21
Show Gist options
  • Save antstanley/4bbc1683d248767e86df9e77063094ab to your computer and use it in GitHub Desktop.
Save antstanley/4bbc1683d248767e86df9e77063094ab to your computer and use it in GitHub Desktop.
Run SvelteKit in AWS Lambda - cdk stack, bundle script and SvelteKit config. Uses SvelteKit's Node.js adapter and Lambda Web Adapter
#! /bin/bash
# - Clear out the dist folder
# - Create new empty folder called svelteKit that will be bundled
# - Run 'vite build' to create production bundle
# - Create run.sh file to be invoked by Lambda using the Lambda Web Adapter
# - Change permissions on newly create run.sh file to allow execution
# - Create package.json file with { "type": "module" } property to ensure all js files use ESM
rm -rf dist
mkdir -vp dist/svelteKit
vite build
echo "Creating run.sh"
printf "#! /bin/bash\nnode index.js" > dist/svelteKit/run.sh
chmod +x dist/svelteKit/run.sh
echo "Creating package.json"
printf '{ "type": "module" }' > dist/svelteKit/package.json
import * as cdk from "aws-cdk-lib";
import { SvelteKitStack, SvelteKitStackProps } from "./svelteKitStack";
import { SSLCertStack } from "./sslStack";
const stackName = "myStack";
const region = process.env.CDK_DEFAULT_REGION ?? process.env.AWS_REGION;
const account = process.env.CDK_DEFAULT_ACCOUNT ?? process.env.AWS_ACCOUNT;
const sslStackProps = {
env: {
region: "us-east-1", // Region MUST be us-east-1 for the SSL certificate
account,
},
variables: { rootDomain: "yourdomain.com", subDomain: "www" },
};
const app = new cdk.App();
const sslStack = new SSLCertStack(app, `${stackName}-ssl`, sslStackProps);
const svelteKitStackProps: SvelteKitStackProps = {
variables: {
domainName: "www.yourdomain.com",
lwaArn: `arn:aws:lambda:${region}:753240598075:layer:LambdaAdapterLayerX86:24`,
},
crossRegionReferences: true,
stacks: { sslStack },
env: { region, account },
};
new SvelteKitStack(app, stackName, svelteKitStackProps);
import * as cdk from 'aws-cdk-lib'
import * as acm from 'aws-cdk-lib/aws-certificatemanager'
import * as route53 from 'aws-cdk-lib/aws-route53'
import { Construct } from 'constructs'
interface SSLCertStackProps extends cdk.StackProps {
variables: {
rootDomain: string
subDomain?: string
}
}
export class SSLCertStack extends cdk.Stack {
public readonly certificate: acm.Certificate
public readonly rootDomain: string
public readonly subDomain?: string
constructor(scope: Construct, id: string, props: SSLCertStackProps) {
super(scope, id, props)
const { rootDomain, subDomain } = props.variables
const hasSubDomain = typeof subDomain === 'string'
const hostedZone = route53.HostedZone.fromLookup(this, 'HostedZone', {
domainName: rootDomain
})
this.rootDomain = rootDomain
this.subDomain = subDomain
this.certificate = new acm.Certificate(this, 'Certificate', {
domainName: hasSubDomain ? `*.${rootDomain}` : rootDomain,
validation: acm.CertificateValidation.fromDns(hostedZone)
})
new cdk.CfnOutput(this, 'CertificateArn', {
value: this.certificate.certificateArn
})
}
}
import adapter from '@sveltejs/adapter-node'
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
/** @type {import('@sveltejs/kit').Config}*/
const config = {
kit: {
adapter: adapter({
out: 'dist/svelteKit'
})
},
preprocess: sequence([vitePreprocess()])
}
export default config
import * as cdk from "aws-cdk-lib";
import * as cloudfront from "aws-cdk-lib/aws-cloudfront";
import * as origins from "aws-cdk-lib/aws-cloudfront-origins";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as logs from "aws-cdk-lib/aws-logs";
import * as route53 from "aws-cdk-lib/aws-route53";
import * as targets from "aws-cdk-lib/aws-route53-targets";
import { Construct } from "constructs";
import * as path from "node:path";
import type { SSLCertStack } from "./sslStack";
export interface SvelteKitStackProps extends cdk.StackProps {
variables: {
domainName: string;
environmentName: string;
lwaArn: string; // https://github.com/awslabs/aws-lambda-web-adapter
};
stacks: {
sslStack: SSLCertStack;
};
}
export class SvelteKitStack extends cdk.Stack {
constructor(scope: Construct, id: string, props: SvelteKitStackProps) {
super(scope, id, props);
const { lwaArn, domainName } = props.variables;
const { sslStack } = props.stacks;
const { stackName } = props;
// adding lambda layer https://github.com/awslabs/aws-lambda-web-adapter
const webAdapterLayer = lambda.LayerVersion.fromLayerVersionArn(
this,
"LambdaWebAdapter",
lwaArn,
);
const svelteKitFn = new lambda.Function(this, "SvelteKit", {
runtime: lambda.Runtime.NODEJS_20_X,
code: lambda.Code.fromAsset(
path.join(__dirname, "..", "dist", "svelteKit"),
),
handler: "run.sh",
timeout: cdk.Duration.seconds(29),
memorySize: 256,
layers: [webAdapterLayer],
logRetention: logs.RetentionDays.ONE_WEEK,
environment: {
AWS_LAMBDA_EXEC_WRAPPER: "/opt/bootstrap",
PORT: "8080",
ORIGIN: `https://${domainName}`,
},
});
const svelteKitFnUrl = svelteKitFn.addFunctionUrl({
authType: lambda.FunctionUrlAuthType.NONE,
});
const svelteKitOrigin = new origins.HttpOrigin(
cdk.Fn.select(2, cdk.Fn.split("/", svelteKitFnUrl.url)),
);
const cloudFront = new cloudfront.Distribution(this, "CloudFront", {
domainNames: [domainName],
certificate: sslStack.certificate,
defaultBehavior: {
origin: svelteKitOrigin,
allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
},
});
const hostedZone = route53.HostedZone.fromLookup(
this,
"cloudFrontHostedZone",
{
domainName: sslStack.rootDomain,
},
);
new route53.ARecord(this, "cloudFrontHostedZoneSiteRecord", {
recordName: domainName,
zone: hostedZone,
target: route53.RecordTarget.fromAlias(
new targets.CloudFrontTarget(cloudFront),
),
});
new cdk.CfnOutput(this, "CloudFrontDomainName", {
value: cloudFront.domainName,
});
new cdk.CfnOutput(this, "CustomDomainName", {
value: domainName,
exportName: `${stackName}-custom-domain`,
});
new cdk.CfnOutput(this, "svelteKitFnArn", {
value: svelteKitFn.functionArn,
});
new cdk.CfnOutput(this, "svelteKitFnUrl", {
value: svelteKitFnUrl.url,
exportName: `${stackName}-url`,
});
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment