カテゴリー
SvelteKitとAWS Lambda@Edgeで始めるサーバーレスなハイブリット(SSR/SSG)ウェブページを楽々作成する
※ 当ページには【広告/PR】を含む場合があります。
2023/03/21

SvelteKitでAWSにホストする感じのSSR/SSGできるハイブリッドなウェブサイトを構築するまでを解説します。
以降では以下のポイントの順当に説明してまいります。
1. SvelteKitの開発環境の導入
2. AWSウェブサイトとして利用する際のadapter-nodeのデメリット
3. AWS向けのアダプターの使い方・実装方法
4. Serverless FrameworkでのLambda@Edgeの使い方
それでは早速、SvelteKitの専用アダプターによるビルドからAWSへのデプロイまでを順を追って解説していきます。
SvelteKitの導入の始め方
SvelteKitはウェブサイトのようなページを管理するアプリ開発に特化したSvelte&Vite製のフルスタックフレームワークです。
純粋なSvelteアプリよりも少しだけ学習の難易度は高くなりますが、非常に軽量で高速なウェブサイト開発が可能になっているのが特長です。
SvelteKitプロジェクトの作成
四の五の言うより、やりながらどのようなものかを理解していったほうが早いので、早速プロジェクトを作成してみましょう。
$ npm create svelte@latest sveltekit-app
Need to install the following packages:
create-svelte@3.1.2
Ok to proceed? (y) y
create-svelte version 3.1.2
┌ Welcome to SvelteKit!
│
◇ Which Svelte app template?
│ Skeleton project
│
◇ Add type checking with TypeScript?
│ No
│
◇ Select additional options (use arrow keys/space bar)
│ none
│
└ Your project is ready!
Install community-maintained integrations:
https://github.com/svelte-add/svelte-add
Next steps:
1: cd sveltekit-app
2: npm install (or pnpm install, etc)
3: git init && git add -A && git commit -m "Initial commit" (optional)
4: npm run dev -- --open
To close the dev server, hit Ctrl-C
Stuck? Visit us at https://svelte.dev/chat
インストール途中で、初期化オプションが聞かれると思いますが、ここでは
Skelton > No type-checking > No additional option
$ cd sveltekit-app
$ yarn install
$ yarn dev
VITE v4.1.4 ready in 587 ms
➜ Local: http://localhost:5173/
➜ Network: http://172.22.0.2:5173/
➜ press h to show help
11:53:18 AM [vite-plugin-svelte] ssr compile in progress ...
11:53:18 AM [vite-plugin-svelte] ssr compile done.
package files time avg
sveltekit-app 3 93.2ms 31.1ms
デフォルトではポート
5173
http://localhost:5173

adapter-nodeを使ったExpress-SSRサイトを構築する方法
※ デメリットを浮き彫りにするため、最初に
adapter-node
nodejsアプリ用のアダプターである
前回で言えば、adapter-nodeを使ってnodejsアプリをバイナリ化できるかを検証するのが趣旨だったのですが、どちらかといえばExpressなどでサーバー側のSSRサイトを作るほうがそもそもの目的です。
$ yarn add @sveltejs/adapter-node -D
アダプターインストール後に、
svelte.config.js
//import adapter from '@sveltejs/adapter-auto';
import adapter from '@sveltejs/adapter-node';
import { vitePreprocess } from '@sveltejs/kit/vite';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: [vitePreprocess()],
kit: {
adapter: adapter(),
}
};
export default config;
ルートフォルダに
.env
DEV_CUSTOM_PORT=5173
その後、
yarn build
build
index.js
handler.js

+ index.js:
SvelteKit標準のサーバーアプリ本体
+ handler.js:
Express等のカスタムサーバー向けのミドルウェア
で使い分けることができます。
今回は
index.js
handler.js
Expressサーバーとして試してみる
ものは試しで、
handler.js
まずはExpressを導入します。
$ yarn add express
$ yarn add @types/express -D
サーバー本体となるJSファイルを作成しましょう。
$ touch server.js
このファイルの中身を簡単に試してみます。
import express from "express";
import { handler } from './build/handler.js';
const port = 5173;
const endpoint = `http://localhost:${port}`;
const app = express();
app.use(handler);
app.listen(port, () => {
process.stdout.write(`\x1b[0;32m\x1b[6m👇をCtrl+クリックしてブラウザで開こう!\x1b[0m\n\x1b[0;43;1;37m${endpoint}\x1b[0m`);
});
これで
http://localhost:5173
結論から言うと、こうなってしまうと
大人の事情で、どうしても典型的なExpressウェブサイトとして構築しなくてはならない場合には、以下のように
SSRレンダリング
このViteベースのSSR方式を採用する場合、Expressミドルウェアによる独自ハイドレーションの実装が必要になり、開発者側の負担が大きくなるデメリットがあります。
では、もっと"Express寄り"のやり方として、
Svelte用のView Engineは例えば以下のようなものです。
ただしこちらも、Svelteのビルドリソースには使えても、既にViteで低レベルな独自コードで出力されているSvelteKitのビルドリソースにはそのまま使えないため、「View Engineを使うならSvelteKitを最初から使うな」と言われれているようなものです。
このような理由から、SvelteKitでSSRウェブページを作ると決めた時点で、
AWS向けのAdapterでSvelteKit-SSRサイトを構築する方法
この節からが本題です。
数ある
ただし、公式のお墨付き(?)なのかは分かりませんが、以下のアダプターを参考に自作できると紹介されています。
おそらくこのサンプルを見ても、
AWS-CDK
また、この程度のサーバーレスアーキテクチャであれば、
AWS-CDK
同様のアーキテクチャを「Serverless Framework」的に解説されている技術記事がZennで公開されており、こちらもとても参考になります。
ただし、Svelte準拠の認証ライブラリ・「sk-auth」を含む内容ですので、より実践的な中級者向けの記事になっています。
本記事では、もう少しハードルが低い、以下のAWS Serverless Adapoterを使ったSSRサイトの構築を簡単に紹介します。
ここで紹介されているサーバーレス設計図は以下のような模式図になっています。

ここでのポイントはリソースごとにアクセスさせるURLオリジンを
Lambda@Edgeを使って同じようなことを以前以下の記事でも紹介したので興味があれば一読ください。
Lambda@Edgeを使うことで、CloudFrontへ複数のキャッシュビヘイビアをオリジンごとに細かく設定しなくても良くなります。
ただし、サーバーレス設計をスッキリさせることができる一方で、Lambda@Edgeを使うには少し面倒かもしれない制約が付きます。
1. Lambda@Edge用にIAM:UpdateAssumeRolePolicyポリシーの追加が必要
2. 基本的にus-east-1リージョン限定
3. スタックの削除の手続きが他より多い
ではこのAWS用のアダプターを使って実際にデプロイするまで一通りやっていきます。
SvelteKit用の専用アダプターでクライアント/サーバー側のリソースをビルド
パッケージのGitHubプロジェクトを見ながらアダプターを写本して、後でカスタマイズしやすくしても良いのですが、最初は基本的なアダプターの使い方だけを確認してみます。
まずはアダプターをインストールしましょう。
$ yarn add @yarbsemaj/adapter-lambda -D
インストールしたら、
svelte.config.js
import { vitePreprocess } from '@sveltejs/kit/vite';
//👇追加
import serverless from '@yarbsemaj/adapter-lambda';
/** @type {import('@sveltejs/kit').Config} */
const config = {
preprocess: [vitePreprocess()],
kit: {
//👇置き換え
adapter: serverless(),
//...
}
};
export default config;
これで準備は完了です...なんて簡単!
あとはいつもどおり、
vite build
build

ビルド後に
build
この内、AWSへデプロイされるときに必要なフォルダは以下の通りです。
- server:
Lambda(SSR用)にセットされるハンドラ関数(server.js)
- edge:
Lambda@Edge(ルート仕分け)に使われるハンドラ(router.js)
- assets:
S3バケットに保管されるクライアントへ提供する静的アセット
- prerendered
プリレンダリングしたページが存在する場合、
S3バケットにindex.html等が保管され、router.jsからクライアントに配給される
デプロイ時に、これら4つのフォルダは適切にCloudFormationの書式で振り分けないといけません。
Serverless Frameworkによるデプロイメント
デプロイ方法にも色々と選択肢が多いですが、AWSでサーバーレスアーキテクチャを組むのには定番の
まずは「Serverless Framework」本体をインストールします。
$ yarn add serverless -D
S3バケットの操作とLambda@Edgeに必要な2つのプラグインも導入します。
$ yarn add @silvermine/serverless-plugin-cloudfront-lambda-edge -D -E
$ yarn add serverless-s3-deploy -D
また、Lambda@Edgeを扱う場合、開発者のAWSロールに
iam:UpdateAssumeRolePolicy
このポリシーが無い場合には、ご自分でルートユーザーに入ってIAMユーザーにポリシーを付与するか、管理者へ問い合わせましょう。

あとはデプロイするとAWS上で動作するとは思いますが、せっかくですのでオリジナルの
serverless.yml
service: 'sveltekit-app'
frameworkVersion: "3"
plugins:
- '@silvermine/serverless-plugin-cloudfront-lambda-edge'
- serverless-s3-deploy
provider:
name: aws
runtime: nodejs16.x
lambdaHashingVersion: 20201221
#👇Lambda@Edgeを使う場合、"us-east-1"が必須
region: us-east-1
stage: ${opt:stage, 'dev'}
#👇①(後述)
package:
individually: true
exclude:
- ./**
include:
- build/server/**
- build/edge/**
#👇②(後述)
custom:
assets:
auto: true
targets:
- bucket:
Ref: StaticAssets
files:
- source: ./build/assets/
globs:
- '**'
empty: true
headers:
CacheControl: max-age=31104000
- source: ./build/prerendered/
globs:
- '**'
empty: true
headers:
CacheControl: max-age=60
#👇③(後述)
functions:
svelte:
handler: build/server/serverless.handler
memorySize: 256
timeout: 15
url: true
cfLambda:
handler: build/edge/router.handler
memorySize: 128
timeout: 1
lambdaAtEdge:
distribution: 'WebsiteDistribution'
eventType: origin-request
resources:
Resources:
StaticAssets:
Type: AWS::S3::Bucket
Properties:
AccessControl: PublicRead
BucketName: ${self:provider.stage}-${self:service}-static-assets
StaticAssetsS3BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: StaticAssets
PolicyDocument:
Statement:
- Sid: PublicReadGetObject
Effect: Allow
Principal: "*"
Action:
- s3:GetObject
Resource:
Fn::Join: ["", ["arn:aws:s3:::", { "Ref": "StaticAssets" }, "/*"]]
#👇④(後述)
WebsiteDistribution:
Type: 'AWS::CloudFront::Distribution'
Properties:
DistributionConfig:
Origins:
-
DomainName: !Select [2, !Split ["/", !GetAtt ["SvelteLambdaFunctionUrl", "FunctionUrl"]]]
Id: default
OriginCustomHeaders:
-
HeaderName: 's3-host'
HeaderValue: '${self:provider.stage}-${self:service}-static-assets.s3.amazonaws.com'
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: 'https-only'
Enabled: true
Comment: '${self:service}_${self:provider.stage}'
DefaultCacheBehavior:
TargetOriginId: default
Compress: true
AllowedMethods:
- DELETE
- GET
- HEAD
- OPTIONS
- PATCH
- POST
- PUT
CachedMethods:
- GET
- HEAD
- OPTIONS
ForwardedValues:
Cookies:
Forward: all
QueryString: True
ViewerProtocolPolicy: 'redirect-to-https'
上記の
①
#...
package:
individually: true
exclude:
- ./**
include:
- build/server/**
- build/edge/**
SSR用の通常のLambda用に
build/server
build/edge
individually: true
なお、SLS V3になってからだと、この
package
詳しくは下の記事をご覧ください。
次に
②
serverless-s3-deploy
#...
custom:
assets:
auto: true
targets:
- bucket:
Ref: StaticAssets
files:
- source: ./build/assets/
globs:
- '**'
empty: true
headers:
CacheControl: max-age=31104000
- source: ./build/prerendered/
globs:
- '**'
empty: true
headers:
CacheControl: max-age=60
#...中略
resources:
Resources:
StaticAssets:
Type: AWS::S3::Bucket
Properties:
AccessControl: PublicRead
BucketName: ${self:provider.stage}-${self:service}-static-assets
#...
リソースとして定義した
StaticAssets
build/assets
build/prerendered
デプロイするたびにリソースは空(=
empty: true
なお、クライアントのブラウザへファイルのキャッシュ時間を伝える
CacheControl
目安として静的アセットファイルは
31104000秒 = 360日
60秒 = 1分
③
#...
functions:
svelte:
handler: build/server/serverless.handler
memorySize: 256
timeout: 15
url: true
cfLambda:
handler: build/edge/router.handler
memorySize: 128
timeout: 1
lambdaAtEdge:
distribution: 'WebsiteDistribution'
eventType: origin-request
#...
通常のLambda関数でSSRする方を
svelte
cfLambda
ハンドラは、それぞれ、
build/server/serverless.handler
build/edge/router.handler
svelte
memorySize
timeout
また言い忘れましたが、暗黙的な運用として、Viteビルドしたリソースで作成したAWS用のハンドラは、
つまり、「API Gateway v1 (RestAPI)」では動作しないため、必ず
このため、明示にHttpAPIであることをLambdaに伝えるためには
url: true
またここでのLambda@Edge・
cfLambda
@silvermine/serverless-plugin-cloudfront-lambda-edge
このプラグインが無くてもLambda@Edgeの設定自体は可能ですが、自作すると色々と煩わしい処理もあるので、もろもろを
lambdaAtEdge
詳しくはプラグインの使い方ガイドをご覧ください。
今回のLambda@Edgeの使い方では「オリジン・リクエスト」(=
eventType: origin-request
Lambda@edgeには4つのトリガーイベントタイプがあります。 具体的な利用例は以前紹介したブログを参考にしてください。
では最後に
④
#...
WebsiteDistribution:
Type: 'AWS::CloudFront::Distribution'
Properties:
DistributionConfig:
Origins:
-
DomainName: !Select [2, !Split ["/", !GetAtt ["SvelteLambdaFunctionUrl", "FunctionUrl"]]]
Id: default
OriginCustomHeaders:
-
HeaderName: 's3-host'
HeaderValue: '${self:provider.stage}-${self:service}-static-assets.s3.amazonaws.com'
CustomOriginConfig:
HTTPPort: 80
HTTPSPort: 443
OriginProtocolPolicy: 'https-only'
このCloudFrontディストリビューションは
「WebsiteDistribution」
キャッシュビヘイビアなどの設定はスタンダードなものですので説明は割愛し、オリジン(Origins)の設定に注目しましょう。
ここでのCloudFrontのオリジンには、デフォルトで、SSR用のLambda・
「svelte」
DomainName
LambdaのURLですが動的に変わるので、CloudFormationの組込み関数をうまく使って、
!Select [2, !Split ["/", !GetAtt ["SvelteLambdaFunctionUrl", "FunctionUrl"]]]
まず
「WebsiteDistribution」
「cfLambda」
#...
OriginCustomHeaders:
-
HeaderName: 's3-host'
HeaderValue: '${self:provider.stage}-${self:service}-static-assets.s3.amazonaws.com'
という部分で
s3-host
余談ですが、CloudFrontのカスタムヘッダーに記述したくない場合には、S3バケットのアドレス情報を記した
.env
router.js
ただし、セキュリティ上の観点から
.env
SvelteKitアプリのデプロイ
ここまでで問題がなければ、
$ npx sls deploy --verbose
でAWSへのデプロイが可能になります。
デプロイ後に生成されたCloudFrontのドメインにアクセスしてみましょう。

ウェブページが見れていたら成功です。
まとめ
今回は、SvelteKitをAWSサーバーレスアーキテクチャでハイブリッドなウェブサイトサービスをデプロイさせる方法を解説しました。
以上の記事の主な要点をまとめると、
1. SvelteKitの開発環境の導入
2. AWSウェブサイトとして利用する際のadapter-nodeのデメリット
3. AWS向けのアダプターの使い方・実装方法
4. Serverless FrameworkでのLambda@Edgeの使い方
となります。
少しボリューム感のある記事内容でしたが、ひとつひとつ噛み砕きながら勉強してみてください。
記事を書いた人
ナンデモ系エンジニア
主にAngularでフロントエンド開発することが多いです。 開発環境はLinuxメインで進めているので、シェルコマンドも多用しております。 コツコツとプログラミングするのが好きな人間です。
カテゴリー