<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: David Porter</title>
    <description>The latest articles on DEV Community by David Porter (@divporter).</description>
    <link>https://dev.to/divporter</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F426193%2Fcb67084e-689c-427a-b89a-c3ad8c38aca9.png</url>
      <title>DEV Community: David Porter</title>
      <link>https://dev.to/divporter</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/divporter"/>
    <language>en</language>
    <item>
      <title>My misadventure with Cloudflare Workers and Vue Serverless Side Rendering</title>
      <dc:creator>David Porter</dc:creator>
      <pubDate>Tue, 29 Dec 2020 14:18:08 +0000</pubDate>
      <link>https://dev.to/divporter/my-misadventure-with-cloudflare-workers-and-vue-serverless-side-rendering-22f7</link>
      <guid>https://dev.to/divporter/my-misadventure-with-cloudflare-workers-and-vue-serverless-side-rendering-22f7</guid>
      <description>&lt;p&gt;&lt;em&gt;In which I talk about my attempt at Vue Serverless Side rendering with Cloudflare Workers&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Just in case the lynch mob arrives I will preface this post by saying that I like the Cloudflare Workers Site concept. This post simply highlights that performing Serverless Side Rendering (SSR) with Cloudfare Workers is still quite challenging.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Tell me about Cloudflare Workers&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;You can think of these like service workers that execute at in the cloud.  Cloudflare have hundreds of edge locations around the world and the location nearest to your user will handle that particular request.  It's very similar to &lt;a href="https://aws.amazon.com/lambda/edge/" rel="noopener noreferrer"&gt;AWS Lambda@Edge&lt;/a&gt;. Not satisfied with that explanation? Here is a &lt;a href="https://developers.cloudflare.com/workers/learning/how-workers-works" rel="noopener noreferrer"&gt;very good explanation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What we're trying to achieve&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Previously I'd written a post on Vue SSR with Lambda@Edge&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/divporter" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F426193%2Fcb67084e-689c-427a-b89a-c3ad8c38aca9.png" alt="divporter"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/divporter/vue-serverless-side-rendering-with-aws-lambda-edge-1ep8" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Vue Serverless Side Rendering with AWS Lambda@Edge&lt;/h2&gt;
      &lt;h3&gt;David Porter ・ Jul 18 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#vue&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#lambda&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#serverless&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ssr&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;There it was suggested I try with Cloudflare Workers. It was actually very easy to setup a &lt;a href="https://developers.cloudflare.com/workers/platform/sites" rel="noopener noreferrer"&gt;Workers Site&lt;/a&gt; with very little config and zero modification to my Vue App. That was very pleasing.&lt;/p&gt;

&lt;p&gt;For those looking to give it a go, if you have lots of assets then consider using &lt;a href="https://help.backblaze.com/hc/en-us/articles/217666928-Using-Backblaze-B2-with-the-Cloudflare-CDN" rel="noopener noreferrer"&gt;Backblaze&lt;/a&gt; to store your many images.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Hurdle 1 - V8 Engine&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;So the problem here is that with traditional Vue SSR, it's expected to be executed in a Node.js environment.  Cloudflare Workers run directly on &lt;a href="https://v8.dev/" rel="noopener noreferrer"&gt;V8&lt;/a&gt;, the engine that runs Chrome and Node.js among others.&lt;/p&gt;

&lt;p&gt;Since Vue 2.5 a &lt;a href="https://ssr.vuejs.org/guide/non-node.html" rel="noopener noreferrer"&gt;simple SSR module&lt;/a&gt; is shipped as well which is mostly environment agnostic so you can do SSR with PHP or Python for example. In essence, this renderer simply renders our Vue app to a string.  So we lose the cool features like template interpolation and injection that come with the standard Renderer and BundleRenderer.&lt;/p&gt;

&lt;p&gt;Thankfully, in response to the challenge below a kind soul (or souls) &lt;a href="https://github.com/l5x/vue-ssr-cloudflare-workers-template/blob/master/vendor/vue-server-renderer/basic.js" rel="noopener noreferrer"&gt;modified the basic renderer&lt;/a&gt; to bring these features back in. &lt;iframe class="tweet-embed" id="tweet-1185301603366178817-52" src="https://platform.twitter.com/embed/Tweet.html?id=1185301603366178817"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1185301603366178817-52');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1185301603366178817&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Hurdle 2 - App size&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Cloudflare Workers are designed to be light and very fast, so Worker scripts are &lt;a href="https://developers.cloudflare.com/workers/platform/limits" rel="noopener noreferrer"&gt;limited&lt;/a&gt; to 1 MB compressed size.  If your site is a simple one then hey, no worries. If you're running a fully armed and operational battle station which exceeds that limit then like me you're out of luck.&lt;/p&gt;

&lt;p&gt;There are probably a few ways to get around this like slimming down our application or storing the bundled app outside of the worker (like in KV or Backblaze) and downloading it at runtime. The first is something we &lt;em&gt;should&lt;/em&gt; do anyway, but who has the time! The second won't work due to the next hurdle&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Hurdle 3 - Execution Time&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A common feature of SSR is fetching data on the server(less) side instead of on the client.  This can also be an SSR killer if the fetch operation is slow, but let's stick with wanting to do our API fetching on the server side now. &lt;/p&gt;

&lt;p&gt;Worker scripts are also capped to at most 50 ms execution time.  This doesn't leave much room for latency on even a simple request let alone a more complex (ie one that requires more time to respond).  &lt;/p&gt;

&lt;p&gt;So looking back at Hurdle 2 if we were to try to pull in our bundled app from KV, then parse it, then render the app HTML we're probably pushing it on the execution time and that's without any data fetching that may occur within the app.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Verdict&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The setup is easier than CloudFront and much more friendly than &lt;a href="mailto:Lambda@Edge"&gt;Lambda@Edge&lt;/a&gt;.  However, like my review of Svelte/Sapper (hey! Two self promos in the one post!) I think Cloudflare Worker sites are cool but just not quite there for Vue SSR. &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/divporter" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F426193%2Fcb67084e-689c-427a-b89a-c3ad8c38aca9.png" alt="divporter"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/divporter/svelte-sapper-vs-vue-2dnk" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Svelte/Sapper vs Vue&lt;/h2&gt;
      &lt;h3&gt;David Porter ・ Sep 1 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#svelte&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#sapper&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#vue&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ssr&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;SSR may not be everything, but it's the great for things like SEO and other applications crawling/scraping your site (eg Facebook crawlers don't wait for client side JS unlike Googlebot).&lt;/p&gt;

</description>
      <category>vue</category>
      <category>cloudflare</category>
      <category>ssr</category>
    </item>
    <item>
      <title>How to build a tiny Flask Docker image for Cloud Run</title>
      <dc:creator>David Porter</dc:creator>
      <pubDate>Sat, 26 Dec 2020 14:51:07 +0000</pubDate>
      <link>https://dev.to/divporter/how-to-build-a-tiny-flask-docker-image-for-cloud-run-349d</link>
      <guid>https://dev.to/divporter/how-to-build-a-tiny-flask-docker-image-for-cloud-run-349d</guid>
      <description>&lt;p&gt;Hey I know what Cloud Run is, just get to the point!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;I'd like to know what Cloud Run is&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/run"&gt;Cloud Run&lt;/a&gt; is a fully managed product within the &lt;a href="https://cloud.google.com/"&gt;Google Cloud (GCP)&lt;/a&gt; suite which allows you to deploy containerized serverless applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Is it like AWS Lambda?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Well... sort of. &lt;a href="https://aws.amazon.com/lambda/"&gt;AWS Lambda&lt;/a&gt; is a fully managed platform for deploying serverless applications.  However with AWS Lambda you only need to write the code.  The GCP equivalent to Lambda is &lt;a href="https://cloud.google.com/functions"&gt;Cloud Functions&lt;/a&gt;.  The AWS equivalent to Cloud Run would be &lt;a href="https://aws.amazon.com/fargate"&gt;Fargate&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why don't I just use AWS Fargate?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The standard way to run a Fargate application is to have at least 1 instance running. As traffic increases the Auto Scaling Group will increase the number of instances to handle the load. If you have an application with lots of traffic and can afford to have an EC2 instance running 24/7 then sure go for it!&lt;/p&gt;

&lt;p&gt;If you'd like to deploy a containerized application with the ability to scale to zero (and so only pay for what you sue) then read on my friend because Cloud Run is for you.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Erm OK, so why don't I just use AWS Lambda&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Hey no arguments here. But there are some cool benefits.&lt;/p&gt;

&lt;h4&gt;
  
  
  Concurrency
&lt;/h4&gt;

&lt;p&gt;Where Lambda will spin up a new instance per request, a single Cloud Run instance can service more than request. When the traffic is too much for that instance a second instance is spun up and so on.  In the simple example of 2 users sending requests at &lt;em&gt;almost&lt;/em&gt; the same time, with Lambda both users experience cold start (waiting for the instances to load up) whereas in Cloud Run only one container will initialize and so the first user still has to wait for the instance to boot up, but the second user enjoys the already warm instance.&lt;/p&gt;

&lt;h4&gt;
  
  
  Developer Freedom
&lt;/h4&gt;

&lt;p&gt;Being a container you can write it in any language with any libraries or even your own binaries.  &lt;/p&gt;

&lt;p&gt;Lambda does allow custom run-times but if that's not enough then a Docker container is what you need.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; During reInvent 2020 AWS announced the ability to deploy your own containers to Lambda. So I guess it's just concurrency?&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;That was confusing, what is Cloud Run?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;It's a platform that allows you to deploy serverless containerized applications where you only get billed while your application is responding to requests. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;span id="docker"&gt;&lt;strong&gt;Dockerfile&lt;/strong&gt;&lt;/span&gt;
&lt;/h2&gt;

&lt;p&gt;Here is an example Dockerfile to build your Flask application.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Those with a keen eye will note this is a multi stage build. This allows us to install everything required to build the app in one container and then copy over the built application into another container and then only deploy precisely what is required to run the app which gives huge savings on size.&lt;/p&gt;

&lt;p&gt;The next thing to note is that the second stage uses a &lt;a href="https://github.com/GoogleContainerTools/distroless"&gt;Distroless&lt;/a&gt; python image as the base image.  This is a super lean image which contains the barest of bare essentials to run our Flask application.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Application Code&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Ok, so let's look at a sample app.  Here is our &lt;code&gt;app.py&lt;/code&gt;, a simple &lt;a href="https://flask.palletsprojects.com/en/1.1.x/"&gt;Flask&lt;/a&gt; app.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Wait a minute, the Dockerfile makes no mention of an &lt;code&gt;app.py&lt;/code&gt; but rather a &lt;code&gt;wsgi.py&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Waitress&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you've used Flask before hopefully you know not to use the built-in development server that comes built in.  Why not you say? Because in plain terms, it only services one request at a time, killing our whole selling point of concurrency.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://docs.pylonsproject.org/projects/waitress/en/stable/"&gt;Waitress&lt;/a&gt;: a production quality WSGI server.  The benefit of waitress over other WSGI servers (eg &lt;a href="https://www.nginx.com/"&gt;NGINX&lt;/a&gt;) is that Waitress is written purely in python so we don't need to install any additional libraries onto our image. This means we can keep it nice and small (Yay!) and serve requests concurrently (Woo!)&lt;/p&gt;

&lt;p&gt;Here's an example &lt;code&gt;wsgi.py&lt;/code&gt; file&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h2&gt;
  
  
  &lt;strong&gt;How to deploy?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Google have plenty of &lt;a href="https://cloud.google.com/run/docs/quickstarts/build-and-deploy#python_1"&gt;documentation&lt;/a&gt; on this so I'm not going rehash all of that. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why bother do all this?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;The python slim Docker image comes to around 60MB compressed, but with Distroless you can get this down to 20MB. That might seem a bit "who cares" but let's look at it from another angle.&lt;/p&gt;

&lt;p&gt;On the front end developers have been scrimping and saving on file sizes for years with uglifying and compression so that pages load faster for the end user. It's all about that Time To First Paint.  Here every KB counts and all sorts of DNS pre-fetching and pre-loading goes on to get the content to the user as quickly as possible.&lt;/p&gt;

&lt;p&gt;Serverless applications are still a relatively new paradigm. Classically (and still currently) a website will be serviced by a dedicated server, meaning that boot times aren't an issue because the server ideally boots once and then runs forever.&lt;/p&gt;

&lt;p&gt;However in the serverless world, when a request comes in a new service is provisioned and the application image is downloaded onto it and then booted up.  So now size matters, and our Distroless app should boot up 3 times faster than the slim app! Then obviously the next area to look at is application start up time, but that's beyond the scope of this article.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Summary&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Multi stage builds, Distroless and Waitress are your friends here.&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>cloudrun</category>
      <category>flask</category>
      <category>python</category>
    </item>
    <item>
      <title>Svelte/Sapper vs Vue</title>
      <dc:creator>David Porter</dc:creator>
      <pubDate>Tue, 01 Sep 2020 15:14:06 +0000</pubDate>
      <link>https://dev.to/divporter/svelte-sapper-vs-vue-2dnk</link>
      <guid>https://dev.to/divporter/svelte-sapper-vs-vue-2dnk</guid>
      <description>&lt;p&gt;&lt;em&gt;In which I highlight the gap between Sapper and Vue&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;So I wrote a lovely article with examples comparing &lt;a href="https://vuejs.org/"&gt;Vue&lt;/a&gt; and friends to &lt;a href="https://svelte.dev/"&gt;Svelte&lt;/a&gt; and &lt;a href="https://sapper.svelte.dev/"&gt;Sapper&lt;/a&gt;.  But twice the draft refused to save and so I gave up.  The main point to get across is that &lt;code&gt;.vue&lt;/code&gt; components are far more readable than &lt;code&gt;.svelte&lt;/code&gt; components.  However on every count Svelte requires less code as is their mantra (and it's a good one). But there is one thing where Sapper doesn't quite measure up.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;SSR and State Management&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;While on the surface Server Side Rendering (SSR) is much easier in Sapper, there is one thing isn't quite there. How do we pass the server state back to the client.  First let's look at Vue&lt;/p&gt;

&lt;h4&gt;
  
  
  Vue SSR &amp;amp; Vuex
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://vuex.vuejs.org/"&gt;Vuex&lt;/a&gt; helps us manage state centrally in our Vue apps. If you follow the &lt;a href="https://vuex.vuejs.org/guide/"&gt;guide&lt;/a&gt; the Vuex store is setup as a singleton.  It looks something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Vuex from 'vuex'

const store = new Vuex.Store({
  ...
})
export default store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is fine on the client, we'll only ever have one store.  However when we run SSR we need to service many clients and requests and we can't mixup the store across those requests, so now it needs to look like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Vuex from 'vuex'

export default function createStore(){
 return new Vuex.Store({
  ...
  })
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can initialise many stores for your many requests. The same principle applies to the Vue app itself as well as &lt;a href="https://router.vuejs.org/"&gt;Vue Router&lt;/a&gt; and &lt;a href="https://apollo.vuejs.org/"&gt;Apollo&lt;/a&gt;. A quick example of that is as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import Vue from 'vue'
import createStore from './store'

export default createApp(){

  const store = createStore()

  const app = new Vue({
    store
  })

  return { app }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;in &lt;code&gt;entry-client.js&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import createApp from './app'

const { app } = createApp()

app.mount("#app")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On the server once our app has processed the route, mutated the state and rendered the HTML we need to pass the Vuex state to the client before it hydrates, otherwise it will hydrate from a fresh store and now your app will be out of step with your store (which is bad).  This is pretty straightforward and you can &lt;a href="https://ssr.vuejs.org/guide/data.html#final-state-injection"&gt;read about it here&lt;/a&gt;. In short the store state is dumped inside &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tags in the server rendered HTML and when Vue is initialised on the client it slurps it up and updates the client state to match.&lt;/p&gt;

&lt;h4&gt;
  
  
  Sapper
&lt;/h4&gt;

&lt;p&gt;If you have no idea what Sapper is, it's the equivalent of &lt;a href="https://nuxtjs.org/"&gt;Nuxt&lt;/a&gt; but for Svelte. The creator, Rich Harris, has a &lt;a href="https://svelte.dev/blog/sapper-towards-the-ideal-web-app-framework"&gt;nice article&lt;/a&gt; introducing Sapper.&lt;/p&gt;

&lt;p&gt;Back to SSR, it's not quite the same in Sapper.  We don't have the same concept of a Vue class that we can initialise and then feed it things like routers, stores and Apollo clients.  In Svelte we have the concept of &lt;a href="https://svelte.dev/tutorial/writable-stores"&gt;stores&lt;/a&gt;, but as singletons.  &lt;/p&gt;

&lt;p&gt;When the stores are written to on the server they hang around between requests. This is bad news, not only can you send a user incorrect data, you could send someone else's data (Yikes!).  So the best approach would be to not mutate the stores on the server. &lt;/p&gt;

&lt;p&gt;But we like having a store on the client and we want the state from the server to come down.  Sapper has a couple of approaches towards achieving this.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Session Data&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;On the server Sapper uses &lt;a href="https://sapper.svelte.dev/docs#Stores"&gt;sessions&lt;/a&gt; for your user data which you can use to set data on the server and then pass down to the client.  &lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Preload&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;In the &lt;a href="https://sapper.svelte.dev/docs/#Preloading"&gt;preload&lt;/a&gt; function we can fetch data server side before rendering. So in a component you can write something like this&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;script context="module"&amp;gt;
  export async function preload(page, session) {
     let animal = "dog";
   return { animal }
  }
&amp;lt;/script&amp;gt;

&amp;lt;script&amp;gt;
  export let animal;
&amp;lt;/script&amp;gt;

&amp;lt;p&amp;gt;
  { animal }
&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it's up to you on the client side to synchronise the store yourself in the &lt;code&gt;onMount&lt;/code&gt; hook. Note that &lt;code&gt;onMount&lt;/code&gt; is never called on the server so you can be confident that it is only running on the client.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;preload&lt;/code&gt; function however is comparable to &lt;a href="https://nuxtjs.org/api/"&gt;asyncData&lt;/a&gt; from Nuxt.js and can run on both server AND the client. You can get around this by using the &lt;code&gt;process.browser&lt;/code&gt; variable and only executing your server logic inside an if block, but this is cumbersome. &lt;/p&gt;

&lt;h4&gt;
  
  
  Apollo Client
&lt;/h4&gt;

&lt;p&gt;A big feature of Apollo is caching. In Vue, if we fetch some data server side with Apollo, we can pass down the Apollo state as we did with the Vuex state.  Like the above, you've got to handle the heavy lifting yourself and there is no recommended/documented way of doing it and some of things you enjoy (or at least I enjoy) in Vue SSR you just won't be able to achieve with Sapper. While it's not critical, without it you're missing out on an advantage of Apollo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Should we all run off and learn Svelte to keep ourselves employed? &lt;/p&gt;

&lt;p&gt;The above state management and singleton problem aside... No, if you can do React or Vue then you're still good.  If you can't do either of those I would strongly recommend learning one if you want a job as a front end developer.  We are in for interesting times however as Svelte production bundles are much much smaller than Vue which means your Time To Interactive (TTI) is also going to be much smaller with Svelte.&lt;/p&gt;

</description>
      <category>svelte</category>
      <category>sapper</category>
      <category>vue</category>
      <category>ssr</category>
    </item>
    <item>
      <title>Vue + Relay + Server Side Rendering (SSR)</title>
      <dc:creator>David Porter</dc:creator>
      <pubDate>Tue, 04 Aug 2020 02:43:12 +0000</pubDate>
      <link>https://dev.to/divporter/vue-relay-server-side-rendering-ssr-24am</link>
      <guid>https://dev.to/divporter/vue-relay-server-side-rendering-ssr-24am</guid>
      <description>&lt;p&gt;&lt;em&gt;In which I describe how to achieve Server Side Rendering with vue-relay&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://graphql.org/"&gt;GraphQL&lt;/a&gt; is a pretty swish alternative to &lt;a href="https://restfulapi.net/"&gt;REST&lt;/a&gt;.  That's not to say it's better or worse, but in certain situations it's pretty handy. &lt;a href="https://relay.dev/"&gt;Relay&lt;/a&gt; is a JavaScript framework designed for hooking up &lt;a href="https://reactjs.org/"&gt;React&lt;/a&gt; applications with a GraphQL API.  GraphQL + React + Relay are the foundations of the current Facebook application. All three are open-sourced, developed by Facebook and designed for Facebook. This means that whilst they are strongly supported and maintained they are primarily geared towards Facebook's needs and concerns.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why not use Apollo?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Well, my recommendation would be to use &lt;a href="https://apollo.vuejs.org/"&gt;Apollo&lt;/a&gt;. It's flexible, easy to get started and most pertinent to this article, it's SSR ready.  There is a nice article at &lt;a href="https://www.howtographql.com/react-apollo/0-introduction/"&gt;How To GraphQL&lt;/a&gt; which explains the difference between Apollo, React and urql. &lt;/p&gt;

&lt;p&gt;In summary Relay is very rigid and demands strict protocols to be followed.  This is handy for Facebook so that amongst all their developers Relay is used consistently across the board. For developers starting out it can be a steep learning curve and for small applications is probably not worth the hassle. It also requires a strong coupling between the API and the client.  That's not such a bad thing, but quite a departure from REST.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pushing on with vue-relay
&lt;/h2&gt;

&lt;p&gt;So for whatever reason, we're sticking with Relay and we're going to use &lt;a href="https://github.com/vue-relay/vue-relay"&gt;vue-relay&lt;/a&gt;.  &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Assumptions&lt;/strong&gt;
&lt;/h2&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Architecture&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;Although a front-end framework Relay demands that your GraphQL server implements a few things. You can read about these &lt;a href="https://relay.dev/docs/en/graphql-server-specification"&gt;here&lt;/a&gt;.  In short you must:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Implement the Node interface&lt;/li&gt;
&lt;li&gt;Implement a refetch mechanism based on the global ID&lt;/li&gt;
&lt;li&gt;Implement a connection model for slicing and paginating through queries that return lists&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Note that you don't need to do these from scratch.  Whichever language (Node.js, Python, Go, etc) you have used to implement your GraphQL server with, it's highly likely that implementations exist for supporting relay.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Server Side Rendering (SSR)&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;This guide assumes that you've got SSR in place, but you can't seem to hook it up with relay. If you don't already have SSR implemented then checkout out the &lt;a href="https://ssr.vuejs.org/"&gt;Vue SSR Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you don't have a server then I've written a post on how to do it with AWS &lt;a href="mailto:Lambda@Edge"&gt;Lambda@Edge&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/divporter" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AJ3suv9g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--ujt-_FMb--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/426193/cb67084e-689c-427a-b89a-c3ad8c38aca9.png" alt="divporter image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/divporter/vue-serverless-side-rendering-with-aws-lambda-edge-1ep8" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Vue Serverless Side Rendering with AWS Lambda@Edge&lt;/h2&gt;
      &lt;h3&gt;David Porter ・ Jul 18 '20 ・ 9 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#vue&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#lambda&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#serverless&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ssr&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h4&gt;
  
  
  &lt;strong&gt;QueryRenderer&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;I also assume that you know how to use &lt;a href="https://github.com/vue-relay/vue-relay"&gt;vue-relay&lt;/a&gt;.  If you don't, first  give the docs a read.  Also read and re-read the &lt;a href="https://relay.dev/docs/en/introduction-to-relay"&gt;Relay docs&lt;/a&gt;. Relay is so rigid that every last detail counts.   &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;GraphQL schema&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Not SSR specific, but worth a mention.  When working with relay you need to compile your queries first with &lt;a href="https://relay.dev/docs/en/graphql-in-relay.html#relay-compiler"&gt;relay-compiler&lt;/a&gt;.  Another reason why I would recommend Apollo, but it does help you fix up any mistakes in your schema and queries and if you're like me you make loads.&lt;/p&gt;

&lt;p&gt;In order to run the compiler you need the API schema in a &lt;code&gt;schema.graphl&lt;/code&gt; file accessible from your client side code. If your schema is defined in code, then you'll need to export the compiled schema. As an alternative the smart cookies at &lt;a href="https://www.prisma.io/"&gt;Prisma&lt;/a&gt; have developed a &lt;a href="https://github.com/prisma-labs/get-graphql-schema"&gt;tool&lt;/a&gt; to introspect your GraphQL API and spit out the schema for you. Very convenient.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Prefetching&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A good starting place is fetching the data.  I'm going to use &lt;code&gt;serverPrefetch&lt;/code&gt; which is available in components for Vue versions 2.6.0+. I recommend you do too, because implementing it yourself is cumbersome, but not impossible.  &lt;/p&gt;

&lt;p&gt;&lt;code&gt;relay-runtime&lt;/code&gt; provides a &lt;code&gt;fetchQuery&lt;/code&gt; method for manually querying your GraphQL API.  &lt;code&gt;fetchQuery&lt;/code&gt; requires the same things that the &lt;code&gt;QueryRenderer&lt;/code&gt; requires.  An environment, a query and some variables.  So it would look something like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { fetchQuery } from 'vue-relay'
...
serverPrefetch(){
  return fetchQuery(this.environment, this.rootQuery, this.variables)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Environment&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;If you've followed the &lt;a href="https://relay.dev/docs/en/network-layer"&gt;Relay Guide&lt;/a&gt; to setting up the Environment, then I'm going to make a few tweaks. We're going to define the Relay Environment within &lt;a href="https://vuex.vuejs.org/"&gt;Vuex&lt;/a&gt; store.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Stepping through the important parts we've set up the initial state like so&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;state: () =&amp;gt; ({
    environment: null,
    cache: new QueryResponseCache({ size: 250, ttl: 60*1000 })
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We start with &lt;code&gt;environment&lt;/code&gt; set to null and setup the &lt;code&gt;cache&lt;/code&gt; using the &lt;code&gt;QueryResponseCache&lt;/code&gt; constructor provided by &lt;code&gt;relay-runtime&lt;/code&gt;. It will become clear why soon.&lt;/p&gt;

&lt;p&gt;Next we define an &lt;code&gt;init&lt;/code&gt; action. This will setup the &lt;code&gt;environment&lt;/code&gt; using the &lt;code&gt;cache&lt;/code&gt;. We will use &lt;code&gt;Environment&lt;/code&gt; from &lt;code&gt;relay-runtime&lt;/code&gt; which looks like this.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const environment = new Environment({
  network,
  store
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;for the &lt;code&gt;network&lt;/code&gt; part we supply a network layer using &lt;code&gt;Network.create()&lt;/code&gt; again from &lt;code&gt;relay-runtime&lt;/code&gt;. The things to note here are that instead of defining the &lt;code&gt;cache&lt;/code&gt; in the global scope we reference the &lt;code&gt;cache&lt;/code&gt; that we defined in the Vuex &lt;code&gt;state&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const cache = state.cache;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The cache is important because we are going to populate it with the result from our GraphQL API. Then when hydration occurs on the client the &lt;code&gt;QueryRenderer&lt;/code&gt; will retrieve the results from the cache.&lt;/p&gt;

&lt;p&gt;Next, you'll notice that unlike the Relay documentation I have opted to use &lt;code&gt;axios&lt;/code&gt; over &lt;code&gt;fetch&lt;/code&gt;. This is because &lt;code&gt;axios&lt;/code&gt; works on both the client and the server. You could use &lt;code&gt;fetch&lt;/code&gt; if you used something like &lt;code&gt;node-fetch&lt;/code&gt; on the server.&lt;/p&gt;

&lt;p&gt;Then at the end we update the &lt;code&gt;environment&lt;/code&gt; in our state&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit("setItem", {key: "environment", value: environment})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;QueryRenderer&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now we need to update the props we pass to the &lt;code&gt;QueryRenderer&lt;/code&gt;.  Here is what it should look like in your &lt;code&gt;template&lt;/code&gt; block.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;query-renderer :environment="environment" :query="rootQuery"
                    :variables="variables" v-slot="{ props, error}"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The &lt;code&gt;environment&lt;/code&gt; props needs to come from Vuex store so we add a &lt;code&gt;computed&lt;/code&gt; property&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;computed: {
  environment(){ return this.$store.state.environment }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;entry-server.js&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here is our entry point to our app for SSR.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Of note here is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await store.dispatch("init")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we are calling the &lt;code&gt;init&lt;/code&gt; action we defined just above to set up our Relay Environment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;context.rendered = () =&amp;gt; {
  const responses = {}
  store.state.cache._responses.forEach( (value, key) =&amp;gt; {
    responses[key] = value
  })
  store.state.cache._responses = responses
  context.state = store.state
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another reason to go for Apollo here.  The &lt;code&gt;QueryResponseCache&lt;/code&gt; that powers our &lt;code&gt;cache&lt;/code&gt; is a Map.  When we render the page we also need to pass the state of the application through. The state is simply serialized with &lt;code&gt;JSON.stringify&lt;/code&gt;.  But you can't serialize a Map. Fortunately for the cache key we use the query and variables as a string. So we can convert this Map to an Object, update the &lt;code&gt;store.state&lt;/code&gt; and set the &lt;code&gt;context.state&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;entry-client.js&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Finally we're going to tie it all together in the hydration step.  On the entry point of our application, if we're hydrating a rendered page we need to update our Vuex store to match.  Normally this is straight-forward but recall that we just butchered the cache in order to serialize it.  Now we need to convert it back to a Map.&lt;/p&gt;

&lt;p&gt;If there is a &lt;code&gt;window.__INITIAL_STATE__&lt;/code&gt; we do the following to convert the &lt;code&gt;_responses&lt;/code&gt; Object back to a Map.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const responses = new Map()
// We initialize the store state with the data injected from the server
if (window.__INITIAL_STATE__.cache &amp;amp;&amp;amp; window.__INITIAL_STATE__.cache._responses){
  for (const [key, value] of Object.entries( window.__INITIAL_STATE__.cache._responses)) {
      responses.set(key, value)
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we override the Vuex state, initialize the &lt;code&gt;cache&lt;/code&gt; and update the &lt;code&gt;cache._responses&lt;/code&gt; with the Map we just created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;store.replaceState(window.__INITIAL_STATE__)
store.commit("setItem", {key: "cache", value: new QueryResponseCache({ size: 250, ttl: 60*1000 }) })
store.commit("setCacheResponses", responses)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then finally call the &lt;code&gt;init&lt;/code&gt; action to set up the Relay Environment. &lt;/p&gt;

&lt;p&gt;As this is a simplified application, if yours is more complex you need to ensure that the &lt;code&gt;init&lt;/code&gt; action doesn't undo the work of SSR by resetting anything that was set with &lt;code&gt;store.replaceState&lt;/code&gt;.  You can do this by specifying this in a separate action and dispatching on the client only for example.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Summary&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Unless you're working at Facebook, use Apollo.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>relay</category>
      <category>graphql</category>
      <category>ssr</category>
    </item>
    <item>
      <title>Head Management in Vue Apps</title>
      <dc:creator>David Porter</dc:creator>
      <pubDate>Sat, 25 Jul 2020 12:49:47 +0000</pubDate>
      <link>https://dev.to/divporter/head-management-in-vue-apps-3g79</link>
      <guid>https://dev.to/divporter/head-management-in-vue-apps-3g79</guid>
      <description>&lt;p&gt;&lt;em&gt;In which I go over your options for managing the &amp;lt;head&amp;gt; section in Vue applications&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you've just polished off your first Single Page App (SPA) with Vue, you may have a missed a detail. All your pages have the same title; the title defined in &lt;code&gt;index.html&lt;/code&gt;.  If you don't have this problem then well done to you! If you do have this problem and you want to know what you can do then read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Why do I need to worry about it?&lt;/strong&gt;
&lt;/h2&gt;

&lt;h4&gt;
  
  
  For the client
&lt;/h4&gt;

&lt;p&gt;Not a huge deal, but without head management all of your pages will have the same title, so no matter what page your user is on, the browser tab will read the same.&lt;/p&gt;

&lt;h4&gt;
  
  
  For the web crawler
&lt;/h4&gt;

&lt;p&gt;Oops, this is a big deal.  Having the same title, meta, structured data will be the same for all pages and this will impact your Search Engine Optimisation (SEO). I'm not an SEO expert so I won't dwell too much on why, but at the very least your Google search results will all look the same if your &amp;lt;head&amp;gt; section is identical for all pages.  At the worst Google may treat all your pages as duplicates and only list your home page.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;How do I do it?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before we launch straight into it I'm going to assume you've got some Server Side Rendering (SSR) in place.  If you don't then checkout out the &lt;a href="https://ssr.vuejs.org/"&gt;Vue SSR Guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you don't have a server then I've written a post on how to do it with AWS &lt;a href="mailto:Lambda@Edge"&gt;Lambda@Edge&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/divporter" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AJ3suv9g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--ujt-_FMb--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/426193/cb67084e-689c-427a-b89a-c3ad8c38aca9.png" alt="divporter image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/divporter/vue-serverless-side-rendering-with-aws-lambda-edge-1ep8" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Vue Serverless Side Rendering with AWS Lambda@Edge&lt;/h2&gt;
      &lt;h3&gt;David Porter ・ Jul 18 '20 ・ 9 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#vue&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#lambda&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#serverless&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#ssr&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;It's important to have SSR in place otherwise search engines and other web crawlers will not see your efforts.  They will only see &lt;code&gt;index.html&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Target Output
&lt;/h3&gt;

&lt;p&gt;Here's what we're hoping our SSR rendered application looks when we're done.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="en"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;title&amp;gt;Parent Title&amp;lt;/title&amp;gt;

    &amp;lt;meta charset="utf-8"&amp;gt;
    &amp;lt;meta name="viewport" content="width=device-width, initial-scale=1.0"&amp;gt;

    &amp;lt;meta name="keywords" content="vue head management"&amp;gt;
    &amp;lt;meta name="description" content="Description set by Parent"&amp;gt;

    &amp;lt;script type="application/ld+json"&amp;gt;
        {
        "@context": "http://schema.org",
        "@type": "WebSite",
        "name": "Parent Title",
        "description": "Description set by Parent"
        }
    &amp;lt;/script&amp;gt;

    &amp;lt;link rel="preload" href="/static/js/main.3e60a259.js" as="script"&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;
  &amp;lt;div data-server-rendered="true"&amp;gt;
    &amp;lt;h2&amp;gt;Parent&amp;lt;/h2&amp;gt; 
  &amp;lt;/div&amp;gt;
  &amp;lt;script src="/static/js/main.3e60a259.js" defer&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;1. Mixins&lt;/strong&gt; &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;If you've gone through &lt;a href="https://ssr.vuejs.org/"&gt;Vue SSR guide&lt;/a&gt; then you've probably had a crack at this method or at least seen it. &lt;/p&gt;

&lt;p&gt;First build the mixin.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Now use the mixin in a component. First import the mixin as standard. In the created/mounted hooks these mixins will look for a title and/or meta key.  You can either pass a string or a function.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Finally in our SSR html template we have access to our title string and meta object and we can reference these inside mustache tags.  If you need to use the raw output use a triple mustache -&amp;gt; &lt;code&gt;{{{ }}}&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h4&gt;
  
  
  &lt;strong&gt;Advantages&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;We have full control of our head management &lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Drawbacks&lt;/strong&gt;
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Lots of code to get it going&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;2. Vue-Meta&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Part of the &lt;a href="https://nuxtjs.org/"&gt;Nuxt.js&lt;/a&gt; framework. If you don't know what Nuxt.js is here is the summary.  Named after &lt;a href="https://nextjs.org/"&gt;Next.js&lt;/a&gt; for React, Nuxt is a complete framework based on Vue, which includes routing, state management, SSR and more.  Nuxt handles all the heavy lifting so you can rapidly develop fully featured web applications.&lt;/p&gt;

&lt;p&gt;Enough about Nuxt. Vue-Meta is a quick and easy-to-use library for head management. Let's see it in action. &lt;/p&gt;

&lt;p&gt;First include it in your application&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import VueMeta from 'vue-meta'

Vue.use(VueMeta)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here's an example of how you include it in your components.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Note the &lt;code&gt;vmid&lt;/code&gt;, this is important if you have child components that may overwrite the meta information.  If you don't use the same &lt;code&gt;vmid&lt;/code&gt; it will write duplicate tags in the output html (which is bad), so make sure you have matching &lt;code&gt;vmid&lt;/code&gt;s for the same meta &lt;code&gt;name&lt;/code&gt;.  The same goes for script tags with &lt;code&gt;application/json+ld&lt;/code&gt; types which house structured data.&lt;/p&gt;

&lt;p&gt;Now to include this in our SSR we need to make a little change to our &lt;code&gt;entry-server.js&lt;/code&gt; file and add the &lt;code&gt;vue-meta&lt;/code&gt; results to the context.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;And in &lt;code&gt;index.html&lt;/code&gt; it now looks like this:&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Much nicer! OK, &lt;code&gt;{{{ meta.inject().title.text() }}}&lt;/code&gt; is a bit of a mouthful but now we don't have to write and manage our own mixins.  &lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;3. Vue Helmet&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A nod to React here, &lt;a href="https://github.com/nfl/react-helmet"&gt;React Helmet&lt;/a&gt; is a head management library for React via reusable components.  Vue Helmet is inspired by the React version and there are a couple of implementations floating around. In this example I'm using the &lt;a href="https://github.com/jnields/vue-helmet"&gt;@jnields/vue-helmet&lt;/a&gt; implementation which supports SSR.&lt;/p&gt;

&lt;p&gt;In our components we use it like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Note this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;helmet-provider :context="context"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We need to pass the application context as a prop to HelmetProvider and so we need to modify &lt;code&gt;main.js&lt;/code&gt; a little to pass the context through.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;And finally in &lt;code&gt;index.html&lt;/code&gt;&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;Easier than the mixin option, but in my opinion not as nice as the Vue Meta option. I find it nicer to work with Objects than with components.&lt;/p&gt;

&lt;p&gt;One issue I did find is that if you define a &lt;code&gt;&amp;lt;script type="application/ld+json"&amp;gt;&lt;/code&gt; tag in a parent component and then try it overwrite it in a child component it will generate two script tags. I haven't tested how Google feels about this, but results are likely to be unreliable. If you recall I stressed the importance of setting the &lt;code&gt;vmid&lt;/code&gt; in your Vue Meta objects. Unfortunately we don't have that option here, but perhaps one of us should submit a PR for it.&lt;/p&gt;

&lt;p&gt;Anyway there you have it.  Make your own mind up but I recommend using &lt;a href="https://vue-meta.nuxtjs.org/"&gt;Vue Meta&lt;/a&gt;. If you want to get your hands dirty then go for the mixin option.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>ssr</category>
      <category>seo</category>
    </item>
    <item>
      <title>Vue Serverless Side Rendering with AWS Lambda@Edge</title>
      <dc:creator>David Porter</dc:creator>
      <pubDate>Sat, 18 Jul 2020 13:21:05 +0000</pubDate>
      <link>https://dev.to/divporter/vue-serverless-side-rendering-with-aws-lambda-edge-1ep8</link>
      <guid>https://dev.to/divporter/vue-serverless-side-rendering-with-aws-lambda-edge-1ep8</guid>
      <description>&lt;p&gt;&lt;em&gt;Note, while this post refers to Vue SPAs, the concept is the same for React apps&lt;/em&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/divporter" rel="noopener noreferrer"&gt;
        divporter
      &lt;/a&gt; / &lt;a href="https://github.com/divporter/ssr-lambda-edge" rel="noopener noreferrer"&gt;
        ssr-lambda-edge
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Serverless Side Rendering with Lambda@Edge
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Single Page Apps (SPAs) are great. I'm a big fan. You can offload all that HTML generation to the client, and SPAs make up the 'J' and 'M' in &lt;a href="https://jamstack.org/" rel="noopener noreferrer"&gt;JAM stack&lt;/a&gt;. An important distinction in sites built with JAM stack is that the SPA is served by a CDN and not a traditional web server. The client and server should be completely uncoupled.&lt;/p&gt;

&lt;p&gt;In AWS world you simply upload your SPA to &lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;S3&lt;/a&gt; and serve it with &lt;a href="https://aws.amazon.com/cloudfront/" rel="noopener noreferrer"&gt;CloudFront&lt;/a&gt;. But what do we do about SEO? Well, when GoogleBot crawls the page it will run any synchronous JavaScript (within a time limit) and then crawl the resulting page. Note the synchronous there which means GoogleBot won't see any data that is fetched when a regular client loads the page.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="https://ssr.vuejs.org/" rel="noopener noreferrer"&gt;Server Side Rendering (SSR)&lt;/a&gt;. For the unfamiliar, here's a quick summary. When a user makes a page request instead of serving an empty &lt;code&gt;index.html&lt;/code&gt; and &lt;code&gt;main.js&lt;/code&gt; the server looks at the route, fetches any required data and then renders the HTML from your SPA according to the SPA routing (eg &lt;a href="https://router.vuejs.org/" rel="noopener noreferrer"&gt;Vue Router&lt;/a&gt;) and then serves up nicely rendered HTML. So now when GoogleBot sees your page all of your dynamic content is there.&lt;/p&gt;

&lt;p&gt;Oh but wait... we don't have a server. So we turn to Lambda. Before that, let's look at our options. &lt;/p&gt;

&lt;h4&gt;
  
  
  SSR everything
&lt;/h4&gt;

&lt;p&gt;One option is to do SSR for all page requests that CloudFront receives. A problem there is that SSR isn't fast and when there's data fetching involved it's only as fast as the API it's pulling from. So instead of loading your &lt;code&gt;index.html&lt;/code&gt; page quickly and showing your users a nice loading screen. They just see a blank page for a few seconds. We can easily implement caching so that the first unlucky user has to wait a few seconds and then every subsequent user gets it lightning fast from the CDN.&lt;/p&gt;

&lt;h4&gt;
  
  
  SSR for SEO only
&lt;/h4&gt;

&lt;p&gt;This is the option I will focus on. So a "regular" user gets your &lt;code&gt;index.html&lt;/code&gt; with the standard SPA client side rendering. GoogleBot on the other hand is treated to a server(less) side rendered html page with all of our dynamic content. Likewise we can implement caching so we don't have to waste Lambda resources rendering the same page over and over.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Architecture Decisions&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;There are a couple of ways to do SSR for SEO only. Using run of the mill &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;Lambda&lt;/a&gt; or using &lt;a href="https://aws.amazon.com/lambda/edge/" rel="noopener noreferrer"&gt;Lambda@Edge&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Lambda
&lt;/h4&gt;

&lt;p&gt;In this model, a Lambda is configured as a CloudFront origin and handles any path that is not an api route, static route, or has an extension other than &lt;code&gt;.html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faq7pvk5yfhy5zkpkn1zk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Faq7pvk5yfhy5zkpkn1zk.png" alt="Lambda SSR"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The Lambda determines whether the user is a web crawler using &lt;a href="https://github.com/JefferyHus/es6-crawler-detect#readme" rel="noopener noreferrer"&gt;es6-crawler-detect&lt;/a&gt; for example. If it is a bot, then proceed with SSR. If it's not a bot then we'll need to serve up &lt;code&gt;index.html&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This is pretty straightforward, but to handle requests for things such as &lt;code&gt;favicon.ico&lt;/code&gt; or &lt;code&gt;manifest.json&lt;/code&gt; which typically live at the root level we need to either make sure we configure the cache behaviors to serve them from S3, or serve them from our Lambda (which is a little trickier).&lt;/p&gt;

&lt;h4&gt;
  
  
  Lambda@Edge
&lt;/h4&gt;

&lt;p&gt;Here we leverage the power of &lt;a href="mailto:Lambda@Edge"&gt;Lambda@Edge&lt;/a&gt;. Lambda@Edge is a special type of Lambda in that unlike "regular" Lambda functions which run at the data centre of your specified region Lambda@Edge runs at the CloudFront edge location where the request is made. In principle it should be faster because it's closer to your user.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk3we4r5myv4a66gcpm26.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fk3we4r5myv4a66gcpm26.png" alt="Lambda@Edge SSR"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this scenario we are going to tell CloudFront whether or not look in the S3 bucket in response to the request, based  on the request path and the User-Agent header. So firstly if the path is pointing to a file (eg &lt;code&gt;manifest.json&lt;/code&gt;) then we tell CloudFront to get it from our S3 origin. If it's a request to a page (eg example.com/page) then we need to see if it's a bot or not. If it &lt;em&gt;is&lt;/em&gt; a bot then we perform SSR and return rendered HTML. If it's not a bot, then serve up &lt;code&gt;index.html&lt;/code&gt; from our S3 origin. In comparison to the Lambda model, this lambda doesn't serve up things like &lt;code&gt;manifest.json&lt;/code&gt;, it only does SSR.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Lambda@Edge implementation&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;OK I hear you. Enough is enough, I've set the scene. Show me some code I can use. Let's start with the Lambda@Edge handler.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;p&gt;&lt;em&gt;WARNING&lt;/em&gt;: the response object is very very delicate. For another example refer to the &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-examples.html#lambda-examples-body-encoding-base64" rel="noopener noreferrer"&gt;AWS docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So what is happening? Let's say a request has been made to &lt;a href="https://example.com/page" rel="noopener noreferrer"&gt;https://example.com/page&lt;/a&gt; and CloudFront has been configured to look in our S3 bucket to fulfil this request. Now let's consider two User-Agent scenarios&lt;/p&gt;

&lt;h4&gt;
  
  
  Scenario 1. User-Agent is GoogleBot
&lt;/h4&gt;

&lt;p&gt;Looking at the if statement&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

if ((!path.extname(request.uri) &amp;amp;&amp;amp; !request.uri.startsWith('/api')) || (request.uri === '/index.html'))


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This will evaluate to &lt;code&gt;(true &amp;amp;&amp;amp; true) || false&lt;/code&gt; which is &lt;code&gt;true&lt;/code&gt;.&lt;br&gt;
Then the next one is obviously true&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

if (CrawlerDetector.isCrawler(userAgent))


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;So we're going to be doing some SSR. &lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

if (request.uri === '/index.html')


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This line exists so that Vue router in our SPA treats &lt;code&gt;index.html&lt;/code&gt; as the '/' route. Although not true in this case, it's worth pointing out.&lt;/p&gt;

&lt;p&gt;Alright now to do some SSR.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

const ssrResponse = await new Promise((resolve, reject) =&amp;gt; {
  const renderer = createBundleRenderer(serverBundle, {
    runInNewContext: false, // recommended
    template,
    clientManifest
  })

  renderer.renderToString({}, (err, html) =&amp;gt; {
    if (err) throw err
    let minified = minify(html, {
      caseSensitive: true,
      collapseWhitespace: true,
      preserveLineBreaks: true,
      removeAttributeQuotes: true,
      removeComments: true
    })
    const response = {
      status: '200',
      statusDescription: 'OK',
      headers: {
        'content-type': [{
          key: 'Content-Type',
          value: 'text/html; charset=utf-8'
        }],
        'content-encoding': [{
          key: 'Content-Encoding',
          value: 'gzip'
        }]
      },
      body: zlib.gzipSync(minified).toString('base64'),
      bodyEncoding: 'base64'
    }
    resolve(response)

  }, reject)
})


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The first part is standard SSR according to the &lt;a href="https://ssr.vuejs.org/" rel="noopener noreferrer"&gt;Vue.js SSR Guide&lt;/a&gt;. For more information check it out, it's pretty cool. Skipping over that, let's get down to the response object, it has to be exactly right or CloudFront will error out. It's important to compress the HTML returned in the response body because we've got to limit the generated response to 1 MB. Check out the CloudFront &lt;a href="https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/cloudfront-limits.html#limits-lambda-at-edge" rel="noopener noreferrer"&gt;Quotas&lt;/a&gt; for more information. If your compressed response is over 1 MB then we can handle this another way which I'll cover later.&lt;/p&gt;

&lt;p&gt;Getting back to it, now that the SSR has rendered the HTML and we've generated the response object, now we simply return it. &lt;br&gt;
 CloudFront will then cache the response against the url &lt;a href="https://example.com/page" rel="noopener noreferrer"&gt;https://example.com/page&lt;/a&gt; + User-Agent. So next time GoogleBot comes along it will serve the SSR rendered HTML straight from the cache. Noice!&lt;/p&gt;
&lt;h4&gt;
  
  
  Scenario 2. User-Agent is Mozilla/5.0 etc etc
&lt;/h4&gt;

&lt;p&gt;Now a real user is coming to look at &lt;a href="https://example.com/page" rel="noopener noreferrer"&gt;https://example.com/page&lt;/a&gt;. Although the request url is the same, the User-Agent is different so CloudFront won't serve from the cache. It will make a request to the origin where our Lambda@Edge will intercept it. Looking at the logic.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

if ((!path.extname(request.uri) &amp;amp;&amp;amp; !request.uri.startsWith('/api')) || (request.uri === '/index.html'))


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This is true again.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

if (CrawlerDetector.isCrawler(userAgent))


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;This is false however as we are not crawlers. So nothing left to do but proceed with the request untouched. This means it will continue with its original intentions and look in S3 for the page. As this is an SPA there is no /page folder so it will send back a 404. Typically when hosting SPAs on CloudFront you convert 404s to 200s and serve up &lt;code&gt;index.html&lt;/code&gt; and so for this request the user gets the standard &lt;code&gt;index.html&lt;/code&gt; and the HTML rendering and data fetching occurs on the client side as we intended.&lt;/p&gt;
&lt;h4&gt;
  
  
  Scenario 3. Request is for manifest.json
&lt;/h4&gt;

&lt;p&gt;As this file has an extension it fails the first hurdle and we continue with the request and the file is retrieved from S3 happily.&lt;/p&gt;
&lt;h2&gt;
  
  
  Serverless Implementation
&lt;/h2&gt;

&lt;p&gt;That's great, but how do I set up all this up in CloudFront? This section makes an assumption that you have the following good to go:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;An S3 bucket with your static website files&lt;/li&gt;
&lt;li&gt;An API (optional)&lt;/li&gt;
&lt;/ol&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;Oof! Alright, I'll point out some the key lines in the &lt;code&gt;serverless.yml&lt;/code&gt;. First up, in the function definition we have a &lt;code&gt;lambdaAtEdge&lt;/code&gt; key. While serverless.com now supports Lambda@Edge as a function event, the &lt;code&gt;@silvermine/serverless-plugin-cloudfront-lambda-edge&lt;/code&gt; plugin has been around much longer and as such I've been using it long before Serverless rolled out native support for Lambda@Edge functions. And to be honest despite my efforts I couldn't get the CloudFront event to work with multiple origins. So vive le Silvermine plugin. Anyhoo, this plugin connects the Lambda@Edge function to our CloudFront distribution.&lt;/p&gt;

&lt;p&gt;Which is a great segue to... our CloudFront distribution which we define in the &lt;code&gt;resources&lt;/code&gt; section. Skipping ahead to &lt;code&gt;CacheBehaviours&lt;/code&gt; which is a list of paths and instructions for how CloudFront should handle them. Note these are applied in the order that they're defined. First up is the &lt;code&gt;/api&lt;/code&gt; path. This allows our API to be called under the same CloudFront domain as our front end. If you don't have an API or you don't need/want it living under the same domain then you can delete this block. Last up is the &lt;code&gt;*&lt;/code&gt; path which points to our S3 bucket. Note this section:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

ForwardedValues:
  Headers:
    - 'User-Agent'


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This tells CloudFront to forward the User-Agent and use it as part of the cache key. If we miss this, then we can't determine if we're dealing with users or bots.&lt;/p&gt;

&lt;p&gt;Then in the &lt;code&gt;Origins&lt;/code&gt; section is where we give CloudFront the details of our API (delete if not required) and our S3 bucket (required).&lt;/p&gt;

&lt;p&gt;Finally the last thing of note is the custom error response.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

CustomErrorResponses:
  - ErrorCode: 403
    ResponseCode: 200
    ResponsePagePath: /index.html
    ErrorCachingMinTTL: 5


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is standard SPA configuration stuff so that when we request paths like &lt;a href="https://example.com/page" rel="noopener noreferrer"&gt;https://example.com/page&lt;/a&gt; which aren't actual files (because we've built an SPA), it will serve up &lt;code&gt;index.html&lt;/code&gt; and Vue Router will handle the internal routing.&lt;/p&gt;

&lt;p&gt;So that's it, easy-peasy! OK, it's actually very fiddly and delicate, with lots of moving parts, but when you get it working it's magical.&lt;/p&gt;

&lt;p&gt;Now to tidy up some loose ends.&lt;/p&gt;

&lt;h4&gt;
  
  
  Can I SSR everything with Lambda@Edge?
&lt;/h4&gt;

&lt;p&gt;In this article I focused on only doing SSR if the User-Agent is a web crawler. However, if you want to use Lambda@Edge for all page requests then simply remove the &lt;code&gt;es6-crawler-detect&lt;/code&gt; parts and now all requests will be handled by &lt;a href="mailto:Lambda@Edge"&gt;Lambda@Edge&lt;/a&gt;. It would be a good idea to reduce the &lt;code&gt;MaxTTL&lt;/code&gt; and &lt;code&gt;DefaultTTL&lt;/code&gt; in the &lt;code&gt;CacheBehaviours&lt;/code&gt; for the '*' PathPattern so the data on the dynamic pages is not potentially 2 days old. This is no big deal for crawlers, but for users it's a good idea to give nice fresh data.&lt;/p&gt;

&lt;h4&gt;
  
  
  My SSR rendered HTML is over 1 MB even after compression
&lt;/h4&gt;

&lt;p&gt;No problemo. First you simply need to create a Lambda with API Gateway proxy and put the SSR code in it. Next add it as an origin in your CloudFront distribution with a path like &lt;code&gt;/ssr&lt;/code&gt;. Note that your newly created lambda needs to have a matching stage so that it responds to request at &lt;code&gt;/ssr&lt;/code&gt; (eg abcde12345.execute-api.ap-southeast-2.amazonaws.com/api). Then in your Lambda@Edge function, when you want to do SSR, instead of generating the HTML in the &lt;a class="mentioned-user" href="https://dev.to/edge"&gt;@edge&lt;/a&gt; function you change the origin to the lambda you just created. Instead or generating a response you modify the request like so.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

const ssrDomainName = 'abcde12345.execute-api.ap-southeast-2.amazonaws.com'
      if (request.uri === '/index.html'){
        request.uri = '/'
      }
      request.origin = {
        custom: {
          customHeaders: {},
          domainName: ssrDomainName,
          keepaliveTimeout: 5,
          path: '/ssr',
          port: 443,
          protocol: 'https',
          readTimeout: 30,
          sslProtocols: ['TLSv1', 'SSLv3']
        }
      }
      request.headers['host'] = [{ key: 'host', value: ssrDomainName}];


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Just like the &lt;code&gt;response&lt;/code&gt; object, the &lt;code&gt;request&lt;/code&gt; object is equally fragile so be careful. In the solution in this article we returned the response, this time we return the request instead, which will then divert the request to our SSR Lambda instead of the S3 Bucket.&lt;/p&gt;

</description>
      <category>vue</category>
      <category>lambda</category>
      <category>serverless</category>
      <category>ssr</category>
    </item>
    <item>
      <title>Deploying a Flask App to AWS Lambda</title>
      <dc:creator>David Porter</dc:creator>
      <pubDate>Fri, 10 Jul 2020 17:56:25 +0000</pubDate>
      <link>https://dev.to/divporter/deploying-a-flask-app-to-aws-lambda-5em0</link>
      <guid>https://dev.to/divporter/deploying-a-flask-app-to-aws-lambda-5em0</guid>
      <description>&lt;p&gt;&lt;em&gt;In which I compare deploying Flask Apps to AWS Lambda with Zappa and Serverless Framework&lt;/em&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/divporter" rel="noopener noreferrer"&gt;
        divporter
      &lt;/a&gt; / &lt;a href="https://github.com/divporter/flask-lambda" rel="noopener noreferrer"&gt;
        flask-lambda
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Deploying a Flask App to AWS Lambda
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;One of the first programming languages I learned was Python.  The first version (v0) of my hobby site &lt;a href="https://veganfeeds.com.au" rel="noopener noreferrer"&gt;VeganFeeds&lt;/a&gt; however was written in PHP with an Apache server connected to a PostgreSQL database serving up dynamically rendered HTML. In the second version, confusingly named v1, I moved from the traditional LAMP stack (or LAPP in my case) to the &lt;a href="https://jamstack.org/" rel="noopener noreferrer"&gt;JAM stack&lt;/a&gt;. Being familiar with Python, I decided that &lt;a href="https://flask.palletsprojects.com/en/1.1.x/" rel="noopener noreferrer"&gt;Flask&lt;/a&gt; would be the 'A' in my JAM stack.&lt;/p&gt;

&lt;p&gt;After having built the Flask app, instead of continuing with a traditional web server I decided to give the serverless approach a try. Now that I've finished my trip down memory lane, let's skip ahead to deploying a Flask app to &lt;a href="https://aws.amazon.com/lambda/" rel="noopener noreferrer"&gt;AWS Lambda&lt;/a&gt;.  &lt;/p&gt;

&lt;p&gt;While &lt;a href="https://aws.amazon.com/serverless/sam/" rel="noopener noreferrer"&gt;SAM CLI&lt;/a&gt; can deploy Python Lambdas, you can't deploy a Flask app straight out of the box. Although you can with a little help from &lt;a href="https://github.com/revmischa/flask-serverless" rel="noopener noreferrer"&gt;flask-serverless&lt;/a&gt;. I have not used it personally so I won't review it here.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Zappa&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Given an existing Flask app, &lt;a href="https://github.com/Miserlou/Zappa" rel="noopener noreferrer"&gt;Zappa&lt;/a&gt; is the easiest way to get your Flask app up there. And when I first discovered Zappa I knew next to nothing about the AWS ecosystem and so it was a friendly resource to enable me to get my Flask app out into the wild.&lt;/p&gt;

&lt;p&gt;It is as simple as:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ pip install zappa
$ zappa init
$ zappa deploy


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Unfortunately though it's never that easy. The above assumes that you have installed your dependencies in a virtual environment within the project directory.  So if you haven't done that you will get an error like below: &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Error: Zappa requires an active virtual environment!


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;So let's remedy that by setting up a virtual environment and installing our dependencies into it.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ python3 -m venv venv
$ source venv/bin/activate
$ pip install zappa flask 


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;OK, so now we're ready to deploy. If we run &lt;code&gt;zappa deploy&lt;/code&gt; again we now face a new error.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Error: This application is already deployed - did you mean to call update?


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;As featured as Zappa is, I find having separate commands for an initial deploy and updating an existing deployments annoying.  Anyway, let's do it.  Let's call &lt;code&gt;zappa update&lt;/code&gt;.  And hooray it works.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Your updated Zappa deployment is live!: https://051zc5ynck.execute-api.ap-southeast-2.amazonaws.com/dev


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's have a look in the browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmejpw1wkjur2ak46wq33.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fmejpw1wkjur2ak46wq33.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;br&gt;
Yep! Looks good.&lt;/p&gt;

&lt;p&gt;Chances are though, if you're building an app beyond "hello world" you may end up adding a dependency that works fine on your development OS but is incompatible in the Lambda environment. Then you're going to need to mimic the Lambda environment when installing your dependencies with docker using the very handy &lt;a href="https://hub.docker.com/r/lambci/lambda/" rel="noopener noreferrer"&gt;Lambda Docker images&lt;/a&gt; built by the lambci team.  So you could do something like:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ pip freeze &amp;gt; requirements.txt
$ docker run -ti -v $(pwd):/var/task  --rm lambci/lambda:build-python3.6 bash -c "python3 -m venv zappa_venv &amp;amp;&amp;amp; source zappa_venv/bin/activate &amp;amp;&amp;amp; pip install -r requirements.txt"


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now the dependencies are built in the correct environment and when you run &lt;code&gt;zappa deploy&lt;/code&gt; your Lambda should now work again. But that's a great big ugly command. Sure you use a Dockerfile, but if you're unfamiliar with Docker then that's going to make things harder. &lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Serverless Framework&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Now despite all that Zappa bashing it was around when there was little else and really stretched the capabilities of CloudFormation and did a lot of heavy lifting behind the scenes before native support was rolled out as standard.  It also comes with a lot of handy features such as the &lt;code&gt;keep_warm&lt;/code&gt; feature to get around the cold start issue.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://www.serverless.com/" rel="noopener noreferrer"&gt;Serverless Framework&lt;/a&gt; is a more generic tool.  Not only covering languages other than Python, such as Node.js and Ruby, but also other Function-as-a-Service (FaaS) providers such as Google Cloud and Microsoft Azure.&lt;/p&gt;

&lt;p&gt;Installation-wise the Serverless Framework is not as friendly to set up the first time as Zappa. Obviously aside from a Python installation you will also need &lt;a href="https://nodejs.org/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed as a prerequisite.&lt;/p&gt;

&lt;p&gt;Assuming you have node next is to install serverless via npm which ships with Node.js&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ npm install -g serverless


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;-g&lt;/code&gt; flag means it is now available globally and you need not install it again for future projects.  OK, so now what? &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ serverless create --template aws-python3


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;OK cool, so we have a few files now.  &lt;code&gt;handler.py&lt;/code&gt;,&lt;code&gt;.gitignore&lt;/code&gt; and &lt;code&gt;serverless.yml&lt;/code&gt; Focusing on &lt;code&gt;handler.py&lt;/code&gt;, this file reveals the bare bones of a Python Lambda handler. So how do we squeeze our Flask app into this? Well we're actually not going to use it at all. We're going to use a plugin.  The Serverless community is very active with a slew of plugins to keep things nice and simple. The &lt;a href="https://www.serverless.com/plugins/serverless-wsgi" rel="noopener noreferrer"&gt;serverless-wsgi&lt;/a&gt; plugin like Zappa is designed for deploying Python WSGI apps to Lambda and abstracting away the handler protocols.  You'll notice at the bottom of the link that the plugin draws inspiration from Zappa. To add the plugin you'll need to run&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ sls plugin install -n serverless-wsgi


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that &lt;code&gt;sls&lt;/code&gt; is a handy alias for &lt;code&gt;serverless&lt;/code&gt;.&lt;br&gt;
Next we need to add a few lines to the bottom of our &lt;code&gt;serverless.yml&lt;/code&gt; file.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

custom:
  wsgi:
    app: app.app


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next we need to modify our &lt;code&gt;serverless.yml&lt;/code&gt; in the functions section so that it looks like this&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

functions:
  hello:
    handler: handler.hello
  api:
    handler: wsgi_handler.handler
    events:
      - http: ANY /
      - http: ANY {proxy+}


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Unlike Zappa there is no dependency on running a virtual environment, however &lt;code&gt;serverless-wsgi&lt;/code&gt; relies on the &lt;code&gt;virtualenv&lt;/code&gt; module. In case you don't have it installed simply run &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ pip install virtualenv


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;followed by&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

$ sls deploy


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;With any luck the output will now show the url for our newly created api&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjd0fz4us2icms4c5yjzt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjd0fz4us2icms4c5yjzt.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if we open in a browser...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc90ovzysajno82eghyaf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fc90ovzysajno82eghyaf.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It works great.  Now what if we want to add a second exclamation and say 'Hello, World!!'.  Unlike Zappa we can just run &lt;code&gt;sls deploy&lt;/code&gt; again. The &lt;code&gt;deploy&lt;/code&gt; command handles both creation and updates. Lovely!&lt;/p&gt;

&lt;p&gt;OK, but what about my dependencies that I need docker for? Well thankfully there's a plugin for that. &lt;a href="https://www.serverless.com/plugins/serverless-python-requirements" rel="noopener noreferrer"&gt;serverless-python-requirements&lt;/a&gt; will do the Docker/lambci stuff for you. It does other nifty stuff too, like zipping them up to reduce their size or even throwing them in a layer for you!&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;What about Chalice?&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://aws.github.io/chalice/" rel="noopener noreferrer"&gt;Chalice&lt;/a&gt; is the offical AWS tool for deploying Python Lambdas.  It has a similar syntax to Flask (and other Python WSGI frameworks), so for Flask users it will feel comfortable.  But unlike Zappa or Serverless you can't use Chalice to deploy an existing Flask app.  You must refactor it first.  &lt;/p&gt;

&lt;p&gt;Chalice is not as configurable as Zappa and Serverless, so you will not get to enjoy all the features of a Flask App along with the flexibility of Zappa or Serverless.  For starting a simple project Chalice will help you get up and running quickly. Then if you find it doesn't meet your needs then you can look to Zappa and Serverless.  &lt;/p&gt;

</description>
      <category>aws</category>
      <category>python</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
