Blog series: from zero to hero! Part 1: Serverless infrastructure on AWS for blog website.

Depascale Matteo
7 min readDec 13, 2022

Build a fast šŸš€ AWS serverless infrastructure from scratch for a blog website using CloudFront in front of Api Gateway, Lambda functions and DynamoDB.

Photo by Austin Chan on Unsplash

For the first part of this series our blog needs

  • to serve content faster around the world šŸŒ;
  • to be free from infrastructure monthly costs, free at least until it hasnā€™t a lot of traffic šŸ’°;
  • admins to create, update and delete blog posts;
  • users to list and read blog posts.

Those are our main requirements, from there I bet we will come up with something new which will go straight to our backlog.

We will build the infrastructure from the ground using AWS CDK.

Infrastructure analysis

Letā€™s review the requirements one by one and analyze how we could satisfy them.

Problem: it needs to be free, at least until it reaches a consistent amount of traffic.
Solution: we should go with serverless services and make use of AWS Free Tier. Going serverless from the start let us scale up whenever we have a lot of traffic and scale down when we do not have any and, of course we wonā€™t pay a dime when our resources are not in use.

Problem: users need to read blog posts, admins need to create them.
Solution: we will build a microservices infrastructure, based on

Problem: we need to minimize latency and serve content around the worldšŸŒŽ.
Solution: we must be able to go global and as near as possibile to our users. To do so we will use Amazon CloudFront as content delivery network (CDN) to make our blog blazingly fast all around the world šŸ”„.

Infrastructure overview

Infrastructure overview with Cloudfront on top of API Gateway, Lambdas, DynamoDB and Cognito
Figure 1: infrastructure overview.
  1. Admins authenticate themselves with Cognito;
  2. Admins call CloudFront domain to create/update/delete blog posts;
  3. CloudFront forward the request to Api Gateway;
  4. Api Gateway turns to a Lambda function to process the request;
  5. The function access the DynamoDB table to save/update/delete blog posts;
  6. DynamoDB stream wakes up and triggers another Lambda function;
  7. Which invalidate the cache of CloudFront. This process is needed because we need to refresh our cache with the new posts;
  8. Users call CloudFront endpoint to list/get blog posts;
  9. CloudFront checks if it has the request cached, if positive then it return the cached response without asking the Api Gateway; if it doesnā€™t have the request cached it forwards the request to Api Gateway;
  10. Which fires a Lambda function to process the request;
  11. The Lambda function queries the DynamoDB posts database and gets the response to Api gateway, CloudFront and the the user.

This is pretty much how our flow will go, before we start we need to address some major points āš ļø:

  • Api Gateway must receive request only from CloudFront, to do that we will have our Api behind an Api Key which will be ingested from CloudFront itself, users wonā€™t know anything and if they try to access the Api Gateway it will returns unauthorized ā›”;
  • Users cannot sign themselves up to Cognito, only admins can create more admins šŸ—ļø.

Database table design

Letā€™s see the requirements:

  • all blog posts must be listed, we probably wonā€™t need to list 100 posts, just a few of the recent ones;
  • we need to be able to get posts by slug (slug: when you click on a post you wonā€™t see an id in your research bar, you will se a text which is called slug);

In term of DynamoDB we need to have this structure:

  • Partition key (PK): itā€™s our primary key and as such we will go by idšŸ¤Æ. We cannot go with the slug because it could change when admins change the title;
  • Global secondary index (GSI): as we need to list every post I went with this configuration:
    - Partition key: 1;
    - Sort key: creation date.
    This means we wonā€™t need to scan any items, we will be able to query them using our index. With this index we are able to list posts by state (draft or published);
  • Global secondary index (GSI): this simply has our slug, which will be unique.

AWS Lambda functions

We need the most optimized functions, to do so we follow a few tricks and best practices written in one of my posts, you can find the link right herešŸ‘‡

Iā€™ve created a simple function to optimize all our lambdas

Cognito user pool stack

Pretty standard stack, it contains:

  • user pool which stores admins information to let them log in;
  • user pool client which we will use to authenticate admins;
  • user pool domain associated.

In the future we will use Lambda triggers, but in the meantime you can learn everything you need to know about them with here šŸ‘‡

Api Gateway stack

This one is really interesting, we need the following components:

  • rest api (obviously šŸ˜…);
  • api key: to allow the access only from CloudFront;
  • cognito authorizer: to authenticate admins, it gets the referenced user pool from Cognitoā€™s stack;
  • request validator: to validate our Apis body and querystrings, itā€™s just an object we will import in our Apis;
  • request model: linked to our validator it creates all our json schema models which will be used to validate the body of the post, put and patch requests.

Only interesting code here is the request model one, letā€™s see what strange thing Iā€™ve done:

What did drive my mind to create api models like this? Because we donā€™t want to touch this file, itā€™s our building block and as such it should receive as little edits as possibile. Doing so we are able to cycle the /models folder and create every model our developers (which is no one šŸ¤”) put in that folder.

Content delivery network to make our api blazingly fast

Well buckle up, this one was really painful but hopefully for us the dirty work has already been done šŸ§Æ.

āš ļøNote: after many tries, the only constructor I was able to work with was L1 which means itā€™s almost bare CloudFormation. I was not able to work with L2 constructors like Distribution because it doesnā€™t let use put a custom header to our origins.

What is worth mentioning:

  • the path we are going to cache are posts for listing our blog posts and posts/* for getting a single post. And you can see the cached methods are just the getters, all of the others end up within the no cache policy.
  • domain name within the origin has a few CloudFormation intrinsic functions which really helped to parse the url to just the domain name one;
  • with custom header we are able to ingest our api key header from CloudFront to Api Gateway

āš ļøNote: usually you want to put the api key in a safe storage like AWS Secret Manager but it costs a few cents per secret per month. So just keep that in mind, I will try not to expose the api key šŸ˜‰.

Cache invalidation design

Starting with a famous quote

There are only two hard things in Computer Science: cache invalidation and naming things.
Figure 2: Phill Karlton quote.

What we need in our infrastructure is:

  • DynamoDB table with stream enabled, which we already have āœ….
  • Lambda function with permissions to create invalidation on CloudFront api distribution;
  • Linking those two with an event source thus creating a trigger.

and the code is pretty simple because it just invalidate the cache for posts. We will start by invalidating every time we create, update and delete an item and when our blog is live we can come back and change the logic of this function so it invalidates the specific item path when possible (like if we change only the content we do not need to invalidate the posts* cache but only the posts/{id} cache āœØ.

āš ļøNote: when importing the table you need to import it with fromTableAttributes method and specify the tableStreamArn param. As per this issue.

Conclusion

In this posts we learnt how to create a serverless architecture without having our api to be slow, also we learnt how to secure the access to our api gateway and also to our admins.

You can find the project here https://github.com/Depaa/website-blog-part1šŸ˜‰

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;
  • āœ… 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 Medium āœØ or on LinkedIn šŸ‘‰ https://www.linkedin.com/in/matteo-depascale/.

--

--

Depascale Matteo

Hi I'm MatteošŸ‘‹! Iā€™m an AWS Cloud Engineer and AWS Community Builder passionate about Serverless on AWS. Follow me https://www.linkedin.com/in/matteo-depascale/