Logo Blog Newsletters

Building a High Performing Static Backoffice On AWS With SvelteKit | Part 3

Published on October 3, 2023
~ 7 min read
SvelteKit
Serverless
AWS
SPA
Backoffice
Article cover

Introduction

This is the third part of a really long journey, in this series I talk about all the steps you need to do to create your own blog on AWS. If you didn't follow the first 2 parts you can find them here:

👉 https://cloudnature.net/blog/blog-series-from-zero-to-hero-part-1-serverless-infrastructure-on-aws-for-blogwebsite

👉 https://cloudnature.net/blog/blog-series-from-zero-to-hero-part-2-serverless-backend-api-on-aws-for-blog-website

We are going to create a SPA which host the backoffice for our blog. We are going to do some changes to our infrastructure and api layer, so I won't talk only about FE don't worry 😜.

⚠️Note: I'm not going to explain how the frontend works, I'm sure you guys know it better than me. But you can learn about those topics: ❓ why SvelteKit; ❓ how to create SPA with SvelteKit; ❓ how to deploy SPA to S3 and serve it through Amazon CloudFront; ❓ how to handle authentication and authorization with Amazon Cognito; 🚀 overall performance.

🙏Special thanks to my friend Gianmarco Pettenuzzo for helping me with the frontend, he has done the most of it and it's cool just because of him. Give him some love ❤️


Requirements

As always we are going to list requirements and analyze them one by one, let's start.

Only admins can create blog posts

This is a pretty requirements, which means we need authentication and authorization. How can we do it? We are going to use Amazon Cognito to authenticate users and its groups to authorize api access. As per the SPA we are going to use Amplify with SvelteKit because I find it really smart and relieves loads of logic from the frontend.

Alright so we need an authentication page and some logic to get everything work as smooth as possible, you really don't want to write a post and then find out it cannot be saved because your token has expired ❌.

Admins must be able to:

📃 list posts

📃 create posts

📃 update posts and images

📃 delete posts

📃 publish posts

To be able to do those actions we need a few pages:

1️⃣ list posts: it should be paginated, every post within the list has 🗑️ delete button and ✏️ update button;

2️⃣ create posts: it should be easy to create blog posts. We are going to use Quill as a text editor for blog content and a few inputs like title, description, image, ecc.;

3️⃣ update posts: same as create but it also has the ✅ publish button;

4️⃣ update images: we are going to use S3 presigned url to be safe and sound when admins need to upload images.


Why SvelteKit?

Out there there are a lot of frameworks to work with, I know some of them and I'm sure the blog can be build with every single one of them.

We wanted to try something different and explore a few scenarios no one has explored yet, hoping it will probably help us improve our overall frontend know how. Furthermore SvelteKit is less verbose than other frameworks and I feel like its performance could really satisfy my obsession for speed: it should be ⚡lightning fast⚡. Also one thing it does out of the box is pre-fetching once you go over a link with your mouse, a lovely feature 🥰. I took a snapshot of it, keep reading to find it.

Other than that I need SEO, I need lots of SEO!

no one can get enough SEO

And this framework does exactly that… out of the box.

But you are building a SPA, why don't you build a SSR? Really interesting question you got there, the reason is my time to market is tight and my requirements say I need to use AWS which means I would have to create my own infrastructure for supporting SSR in AWS.

⚠️Note: if everything goes as planned in the future you could read a blog post about switching from SPA to SSR. I know the full potential of SvelteKit is when SSR is enabled.

If you want to learn more of SvelteKit here is the starting point https://kit.svelte.dev/, have fun 🎉


Create SPA with SvelteKit

I'm gonna say it! it's……… really easy. You just need to import an adapter. What are adapters? They are plugins which take the build app as input and generate the adapted output for your deployment target.

SPA means we need to bundle a static website with a single file serving content, to build (better yet: adapt) the app we need to install @sveltejs/adapter-static

Edit svelte.config.js like so:

    import adapter from '@sveltejs/adapter-static';
    import { vitePreprocess } from '@sveltejs/kit/vite';

    /** @type {import('@sveltejs/kit').Config} */
    const config = {
        preprocess: vitePreprocess(),
        kit: {
            adapter: adapter({
                fallback: 'index.html'
            })
        },
    };

    export default config;

Perfect but not there quite yet, we need to disable SSR and prerender in src/routes/+page.ts 😩:

    export const prerender = false;
    export const ssr = false;

To test it out you can run a build and, if everything is right, you should see static adapter being used:

build successful


Deploy SPA to S3 and serve it through Amazon CloudFront

This one is a little bit tricky, right now we have the build and we can deploy it to S3 with this command

    aws s3 sync ./build s3://{BUCKET_NAME} --delete
    aws cloudfront create-invalidation --distribution-id {DISTRIBUTION_ID}--paths \"/*\"

which deletes previous items, replace them with the new ones and then invalidate CloudFront cache. Aaaaand voilà 🎉🎉

login page

🔝 our blog works, let's go and see all blog posts (if we have any)

all posts page

Perfect, love to see it. Let's try to refresh that page…..

Cloudfront error

Cloudfront search for /blog/index.html but can't find in S3 because there aren't any files in that directory 🤯.

To fix this error we need to redirect every 403 status code to /index.html:

    const bucket = new Bucket(this, 'bucket', {
        accessControl: BucketAccessControl.PRIVATE,
        blockPublicAccess: BlockPublicAccess.BLOCK_ALL,
        publicReadAccess: false,
    });

    const distribution = new Distribution(this, 'distribution', {
        comment: 'CDN for blog website',
        defaultBehavior: { origin: new S3Origin(bucket) },
        defaultRootObject: 'index.html',
        httpVersion: HttpVersion.HTTP3,
        priceClass: PriceClass.PRICE_CLASS_ALL,
        errorResponses: [
            {
                httpStatus: 403,
                responsePagePath: '/index.html',
                responseHttpStatus: 200,
            },
        ],
    });

Alright time to deploy and wait ⌛. When everything is complete you can test it out going to /blog and, thanks to this fix, it will be redirected to /blog page.

Now that we know our SPA is working we can move on to explain the next step.


Authentication and authorization with Cognito

I wanted to test Amplify with Cognito for SvelteKit, I've already wrote an article about it so if you want to read more you can check this post out 👇

Configure Amplify Authentication With SvelteKit

With that we have set up Amplify and can use its methods. Which methods we need to set up?

  • currentAuthenticatedUser
  • signIn
  • signOut
  • currentSession: to retrieve current session and the id token of it; moreover if the session expires it tries to get a new session using the refresh token, really useful in my opinion 💪

Meanwhile if you are asking yourself how does it work for the backend then you can find the answer in one of the previous posts 👇

Blog Series: From Zero To Hero! Part 2: Serverless Backend Api On AWS For Blog Website

I did also a few upgrades to the backend as well, I got 2 main paths for apis:

  • /admin/posts which contains all administrator apis, which means they need authentication;
  • /posts which contains public apis which don't need any authentication.

Other than that I had to add a few more indexes to our DynamoDB table because:

  • admins can see every post, from drafted ones to published, meanwhile users can only see published posts; which means we need to use state as primary key (pk) and published at as sort key (sk);
  • admin can select featured posts that can be seen from the homepage; which means we need featured as pk and published at as sk.

Upload images with S3 presigned url

This one is the ace up my sleeve. Using S3 presigned urls we can enable admins to upload files, or better yet images, right where we wanted it to be uploaded because we can define the key and the expiration date for that url.

presigned url flow

In this diagram we can actually see the flow admins do when uploading the cover image for a particular post. The code? Again it's just a few lines:

    import { S3Client, PutObjectCommand, PutObjectCommandInput } from '@aws-sdk/client-s3';
    import { getSignedUrl } from '@aws-sdk/s3-request-presigner';

    const client = new S3Client({ region: process.env.REGION });

    const createSignedUrl = async (key: string, type: string, bucketName: string): Promise => {
        const params: PutObjectCommandInput = {
            Bucket: bucketName,
            Key: key,
            ContentType: type,
        };

        const pubObjectCommand = new PutObjectCommand(params);
        const res = await getSignedUrl(client, pubObjectCommand, { expiresIn: 60 * 5 });
        return res;
    };

After doing that remember to enable CORS from S3, otherwise S3 bloks your frontend and you are not able to upload images.


SvelteKit pre-fetching

This is just a praise for a really straightforward feature SvelteKit has out of the box, you just have to add one simple line in app.html

    body data-sveltekit-preload-data="hover"

And the result is pure magic 🌈

SvelteKit prefeching example


Conclusion

We had a lot of fun making the website and learning a few things about SvelteKit, I really liked how it turned out and can't wait to do the customer facing frontend. Now that we have our backoffice ready to create content we just need to build the frontend and improve its performance as much as we can while keeping an eye to our time to market👀.

🙏Huge thanks to Gianmarco Pettenuzzo for helping me out and creating a really reliable website, check him out 🙇.

You can find the project here https://github.com/Depaa/website-blog-part3 😉

This series is literally a journey to production, hence you will see a lot of blog posts, here is a list I'll try to keep updated:

  • ✅ Serverless infrastructure on AWS for blog website;
  • ✅ Serverless backend api on AWS for blog website;
  • ✅ Building a high performing static backoffice on AWS with SvelteKit;
  • ✅ Frontend users website for reading blog posts;
  • ✅ SEO tweaks for blog website;
  • ✅ Analytics and tracking views on our blog website;
  • Infrastructure monitoring and alerting to the blog;
  • Going live with the blog;
  • CICD pipelines for the blog;
  • Disaster recovery, RTO and RPO. Going multiregion with serverless;
  • … you see, there are a lot planned, and if you want to add more points just DM me or comment right below.

Thank you so much for reading! 🙏 I will keep posting different AWS architecture from time to time so follow me on LinkedIn 👉 https://www.linkedin.com/in/matteo-depascale.