The Front Flip: When a DevOps Engineer Enters the JAMStack — Functional Preact/Hugo/AWS Amplify

Florian Dambrine
Dec 18, 2020 · 10 min read
Image for post
Image for post
Photo: BENNI SCHMID — Copyright by https://www.gibbon-slacklines.com/

Relax and put your seat belt on… You’re about to enter my journey of an intense 2 weeks sprint I recently led, swapping my DevOps hat for a front-end one to build a full JAMStack website from the ground up deployed on AWS!

Foreword & Context

This API not only supports page level analysis, but also offers standalone endpoints for text, image and video classification.

It acts as a coordinator, orchestrating the flow of a request within pipelines made of a dozen micro-services until it has a full response that can be sent back to the client.

Image for post
Image for post
Visualization of Page processing steps

When a request gets stuck in the pipeline (no success nor response notification), identifying the culprit in such an infrastructure is like trying to find a needle in a haystack:

  • What system did not respond to the request?
  • Did the request fail in the pipeline or did the caller miss the response?
  • Is the request response simply delayed due to lag in the pipeline?

An API developer would go check the different back ends and look for the raw data corresponding to a request to be able to provide a response after investigation…

But for the rest of us, it sounds more like, “I don’t even know where this data is stored…”

It became clear to me that we needed a Web UI that will help visually display all the data for a request and make it easy for anyone to spot the problem rapidly.

The only thing is that our team is made of everything except front-end engineers. With my recent focus on enhancing team productivity, I took on this mission of building a Web UI from scratch.

Front end for non Front end people…

Image for post
Image for post
Collection of JavaScript Frameworks : Copyright by Jose Aguinaga

We all know how vast the front-end eco-system is: from build tools and asset managers like Webpack, Yarn and Grunt, to frameworks and libraries such as React, Preact, Vue and Angular, and even static site generators (SSG) like Hugo, Gatsby and Jekyll. You have a lot of options to build a website in 2020!

Given that my JS knowledge stopped at simple vanilla JS and at most JQuery, I had to make wise choices to make sure this project would not go over my sprint and would remain within my skill set. Hence I came up with the following statements:

  • Use a JS framework only when dynamism is required (Authentication / Retrieve data from AWS back ends / Manage complex UI interactions)
  • Use plain HTML when the content is static (modern libraries like Animate.style can be leveraged to make your static HTML look more dynamic)

Choosing the Tech

  • JavaScript: Dynamic functionalities are handled by JavaScript. There is no restriction on which framework or library you must use.
  • APIs: Server side operations are abstracted into reusable APIs and accessed over HTTPS with JavaScript. These can be third party services or your custom function.
  • Markup: Websites are served as static HTML files. These can be generated from source files, such as Markdown, using a Static Site Generator.
Image for post
Image for post
Traditional Vs JAMStack — Copyright by https://jamstack.org/

This is something familiar to us as we have built our entire internal documentation using Hugo and the great Hugo-Learn theme !

Now we need to figure out how to plug a JavaScript workflow/framework into something as simple as a static website generator and make both tools co-exist!

Adding an assets pipeline to Hugo

Choosing a JavaScript framework

Digging further in Google drove me to this article, Preact meets CMS: Building Lightweight Portable Widget Components from Zouhir (Podcast available as well), which literally convinced me with Preact-habitat.

Preact-habitat is just an extension to Preact’s render function where I decided to provide more API. The concept of Preact habitat is pretty straight forward and can be summarised as :

> Scan the DOM when the widget’s script is loaded.
> Re-scan again when DOM is fully loaded.
> Allow host CMS page to pass props via HTML.
> Stay < 1KB.

— Copyright by Zouhir

This was the last piece of the puzzle ! Let’s recap where we are at so far with this stack:

Image for post
Image for post
Preact & Hugo stitched together with Webpack

Getting Started — Project Scaffolding

I just had to glue together Hugo/Preact and Webpack to get my own template going (originally forked from the Victor Hugo Netlify template).

################################################################
### Gettings Started - Docker way (no setup required)
################################################################
### Create project scaffolding using preact-cli from custom template$ docker run --rm \
-w /app \
-v $(pwd):/app \
lowess/preact-cli create Lowess/preact-hugo demo-app
### List project folder content$ cd demo-app/### Run project using "npm run start"$ docker run --rm \
--entrypoint="" \
-p 3000:3000 \
-e HOST=0.0.0.0 \
-w /app \
-v $(pwd):/app \
lowess/preact-cli npm run start
## Visit http://localhost:3000

Or simply go check it out on Github pages: https://lowess.github.io/preact-hugo

The Static Content navigation drives you to a list of posts that are written in simple Markdown and generated by Hugo.

Image for post
Image for post
Hugo statically built content

The Dynamic Content navigation loads a dynamic Preact-habitat widget that is built with our JavaScript tool chain and packaged along with the app.

Image for post
Image for post
Preact widget injection in Hugo thanks to Preact-habitat

Hugo JavaScript widget injection with Preact-habitat ?

  • Hugo searches for the layout to use for a given page in a well defined order. In our case, the layouts/dynamic/ section should override the default site behavior to do whatever is necessary to load the JavaScript widget. Here is an example of Hugo layout to do so:
layouts
├── _default // Default layout for static pages and homepage
│ ├── baseof.html // Main website structure
│ ├── list.html // List of articles (eg. /static/)
│ └── single.html // Single article (eg. /static/getting-started)

├── dynamic // Override templates for /dynamic section
│ └── list.html // Defines the logic to load JS widget
...
  • The default template is overridden by dynamic/list.html which defines the main content of the page:
{{ define "main" }}
{{- "<!-- Preact component injection -->" | safeHTML }}
{{ partial "preact.html" (dict
"widget" .Site.Data.webpack.helloWidget
"data" (dict "name" "I am built with Preact")
)
}}
{{ end }}

This file calls a partial template with proper arguments like the widget to load (path to <widget>.js) and the data (props passed as JSON to the widget component).

The partial template preact.html is simply implementing Preact-habitat logic to load and render the widget:

<div class="preact">
{{- "<!-- Preact component -->" | safeHTML }}
{{- $script := .widget }}
{{- $data := .data }}
{{ with $script.css -}}
<link href="{{ relURL . }}" rel="stylesheet">
{{- end }}
{{ with $script.js -}}
{{ with $data -}}
<script type="text/props">
{{ . | jsonify }}
</script>
{{- end }}
<script async src="{{ relURL . }}"></script>
{{- end }}
</div>

Here is what the final render of this HTML page looks like:

<div class="content container-fluid p-0">      
<!-- Preact component injection -->
<div class="preact">
<!-- Preact component -->
<script type="text/props">
{"name":"I am built with Preact"}
</script>

<script async src="/preact-hugo/helloWidget.e5579.js"></script>
</div>
</div>

A Serverless back end with AWS Amplify

Authentication and GraphQL API

Image for post
Image for post
AWS Amplify — An AWS SDK to build full stack application

The AWS Amplify SDK seemed to be a great fit as it offers OAuth authentication using Cognito and supports federation with social providers such as Google.

It also comes with GraphQL API features that let you plug into AWS AppSync to handle the heavy lifting of securely connecting to data sources like AWS DynamoDB or Lambda. It also has a built in feature that will ensure that the user performing the request is currently authenticated in AWS Cognito.

Wow! Lots of concepts to digest! But watching AWS Amplify with Amazon DynamoDB Single Table and Direct AWS Lambda Resolvers with Rick Houlihan was helpful and confirmed this technical choice.

The major key takeaway I learned from this video is the fact that AppSync now seamlessly integrates with Lambda using direct resolvers.

The lambda function will likely act as a giant SWITCH CASE that will, based on the name of the GraphQL query, perform a certain action (query DynamoDB records, fetch content from RDS, pre-sign an S3 URL…). The sky is the limit as long as you are within your VPC and you likely have access to anything you may want!

This means that your back end remains quite simple: A public and secured API Gateway (AppSync endpoint) and your business logic (in a single Lambda).

Here is a diagram that recaps how we deployed this Serverless infrastructure in order to leverage AWS Amplify SDK on the front end:

Image for post
Image for post
AWS Amplify — AppSync Back end / Cognito Authentication layer

We now have all the pieces our application requires to serve the different features we want to implement, so now it’s time to actually write code.

Thinking in JavaScript…

As usual, when you don’t know, you look for best practices in documentation. I quickly understood that the (P)React world offered two ways of doing things:

  • The former way of writing classes
  • The newer way introduced in React v16.8 (2018) of using hooks (which also exist in Preact)

Watching 90% Cleaner React With Hooks by Ryan Florence — React Conf 2018 was the trigger that led me to adopt a functional style using hooks over the former class definitions.

Here an implementation of Amplify authentication using Preact written in a functional style:

Conclusion

This blog post also showcases the fact that static site generators like Hugo can actually be leveraged in more complex JAMStack projects or extended to use cases you might not think of (like turning your Hugo website into a static API according to this Forestry.io blogpost, which is something we also did in this project).

👉 With that, I will let you go JAM on your stack now

Resources

We’re always looking for new talent! View jobs.

Follow us: Facebook | Twitter | Linkedin | Instagram

gumgum-tech

Thoughts from the GumGum tech team

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store