DEV Community

Cover image for A Practical Introduction to Jamstack
Martins Onuoha
Martins Onuoha

Posted on

A Practical Introduction to Jamstack

What is Jamstack

If you’re into Web Development and have worked with one or two Javascript Frameworks at one point, you most likely have come across the terms MERN, MEAN, and, most recently, MEVN Stack. MongoDB serves as your non-relational database in these stacks, while ExpressJS and Nodejs are your web server. One similarity between these stacks is the presence of a web server. This is where the Jamstack differs.

JAM stands for Javascript API Markup, an abbreviation of the technologies used in building a web application. Here’s a pretty straightforward explanation of what Jamstack is from the official Jamstack website:

A modern architecture for creating fast, secure sites and dynamic apps with JavaScript, APIs, and pre-rendered Markup, served without web servers*.*


Why is it necessary

From the definition, “without web servers” is in bold text because this is the basic idea of Jamstack. With Jamstack, front-end developers can build useful, functional web applications without needing a backend.

With just your front-end skill, you can build awesome useful applications thanks to the Jamstack specification.

Some examples of existing sites built with Jamstack are:

Kentico GitHub Community

A gateway to the world of open-source at Kentico.

favicon kentico.github.io

To learn more about Jamstack, check out the official Jamstack website: jamstack.org

A Practical Example

Example app
We’ll be building Chucklarious. Chucklarious is a collection of random Chuck Norris jokes. Not the most useful application, I know, but it’s a basic illustration of what Jamstack is about.

Prerequisites
Basic knowledge of HTML, CSS, and Javascript

Familiarity with Javascript’s fetch API.

We’ll be working with the open-source Internet Chuck Norris Database API.


Folder Structure

We have a basic folder structure, so you can ignore the img folder.

Example folder structure

Markup

From the screenshot of Chucklarious, you’d notice we have a basic structure of top Navbar, Cards, and a FAB at the bottom right.

I’ll be using MaterializeCSS; however, feel free to use whatever you want for styling.

For Content rendering and semantic templating, I’ll be using Handlebarsjs

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>Chucklarious</title>
    <link rel="shortcut icon" href="https://www.demilked.com/magazine/wp-content/uploads/2016/06/gif-animations-replace-loading-screen-14.gif" />
    <link href="./css/style.css" rel="stylesheet" type="text/css" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
    <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

  </head>

Enter fullscreen mode Exit fullscreen mode

If you checked the live example, you would notice a preloader just before the page loads. The Navbar comes right after the preloader.

  <body class="grey lighten-2">
    <div class="center-align" id="preloader"></div>
    ...

Enter fullscreen mode Exit fullscreen mode
...
  <div class="navbar-fixed">
    <nav>
      <div class="nav-wrapper purple lighten-1">
        <ul id="nav-mobile" class="left hide-on-med-and-down">
          <li><a href="https://github.com/martinsOnuoha">GitHub</a></li>
          <li><a href="#">LinkedIn</a></li>
          <li><a href="#">Medium</a></li>
          <li><a href="#">Original</a>
          </li>
        </ul>
      </div>
    </nav>
  </div>
...

Enter fullscreen mode Exit fullscreen mode

Next, we’ll build out content to show the user in case of failed page load.

  <div id="container" class="container">
      <div class="row" id="render_here">
          <div id="failure" class="col s12">
              <h5 class="center-align">This isn't funny we know, but Something went wrong. :(</h5>
          </div>
      </div>
  </div>

Enter fullscreen mode Exit fullscreen mode

For content rendering, we’re using Handlebars. Handlebarjs is a minimal templating engine. Handlebars templates look like regular HTML, with embedded handlebars expressions. Those expressions have a format like {{ some expression }}. These expressions make it easy to render contents dynamically, one of which is {{ each }} which works like a regular for loop in Javascript.

  <script id="entry-template" type="text/x-handlebars-template">
    {{ #each jokes }}
      <div class="col m6 s12">
        <div class="card white hoverable" id="joke{{ id }}">
          <div class="card-content white-text">
            <p class="flow-text">{{ joke }}</p>
          </div>
          <div class="card-action">
            <button id="btn-{{ id }}" class="btn waves-effect waves-light purple lighten-1"
              onclick="lmfao(this.id)">LMFAO
              <i class="material-icons right">star</i>
            </button>
          </div>
        </div>
      </div>
    {{ /each }}
  </script>

Enter fullscreen mode Exit fullscreen mode

Notice we have a button for reacting to jokes. I’ve attached an on-click event to the button, called the LMFAO (terrible choice of name, by the way) function, and passed the joke id as an argument.

  <div class="fixed-action-btn toolbar">
    <a id="menu" class="btn-floating btn-large purple darken-2">
      <i class="large material-icons">menu</i>
    </a>
    <ul>
      <li class="waves-effect waves-light">
        <a href="#!" onclick="reloadJokes()">
          <i class="material-icons">autorenew</i>
        </a>
      </li>

      <li class="waves-effect waves-light">
        <a class="modal-trigger" href="#!" data-target="closeModal">
          <i class="material-icons">power_settings_new</i>
        </a>
      </li>
    </ul>
  </div>

  <!-- Tap Target Structure -->
  <div class="tap-target" data-target="menu">
    <div class="tap-target-content white-text">
      <h5>Info</h5>
      <p>Insert an Info here</p>
    </div>
  </div>
  <!-- end Tap Target -->

Enter fullscreen mode Exit fullscreen mode

We would also need a prompt just before the user closes the application.

  <div id="closeModal" class="modal">
    <div class="modal-content">
      <h4>Sure?</h4>
      <p>You're about to stop laughing...</p>
    </div>
    <div class="modal-footer">
      <a href="#!" class="modal-close waves-effect waves-green btn-flat left">Nah</a>
      <a href="#!" onclick="window.close()" class="waves-effect waves-green btn-flat right">
        Yes, I've had enough
      </a>
    </div>
  </div>

Enter fullscreen mode Exit fullscreen mode

Finally, we include the scripts at the bottom, right before the closing body tag:

  <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.12/handlebars.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
  <script src="./js/script.js"></script>

Enter fullscreen mode Exit fullscreen mode

Styles

Let’s add a bit of styling for the preloader and card content. Add this to style.css:

#failure {
  margin-top: 10em;
  color: #CCC;
  display: none;
}

#preloader {
  position: fixed;
  z-index: 99999999999;
  top: 0;
  left: 0;
  overflow: visible;
  width: 100%;
  height: 100%;
  background: #fff url("https://www.demilked.com/magazine/wp-content/uploads/2016/06/gif-animations-replace-loading-screen-14.gif") no-repeat center center;
}

.flow-text {
  color: grey;
}
.card-content {
  min-height: 220px;
  max-height: 220px;
  overflow-y: scroll;
}

Enter fullscreen mode Exit fullscreen mode

API

I mentioned earlier that we would be using the icndb.comAPI. They provide an endpoint to fetch random jokes while limiting the number of jokes to fetch per request. The endpoint would look like this:

https://api.icndb.com/jokes/random/10

Let’s see what the structure of our JSON response would look like:

{ "type": "success",
  "value": [
    { "id": 489, "joke": "Chuck Norris can write multi-threaded applications with a single thread.", "categories": ["nerdy"] },
    { "id": 513, "joke": "Chuck Norris does not code in cycles, he codes in strikes.", "categories": ["nerdy"] },
    { "id": 342, "joke": "Chuck Norris owns a chain of fast-food restaurants throughout the southwest. They serve nothing but barbecue-flavored ice cream and Hot Pockets.", "categories": [] },
    { "id": 271, "joke": "Staring at Chuck Norris for extended periods of time without proper eye protection will cause blindess, and possibly foot sized brusies on the face.", "categories": [] },
    { "id": 306, "joke": "Scientifically speaking, it is impossible to charge Chuck Norris with &quot;obstruction of justice.&quot; This is because even Chuck Norris cannot be in two places at the same time.", "categories": [] },
    { "id": 303, "joke": "Fact: Chuck Norris doesn't consider it sex if the woman survives.", "categories": [] },
    { "id": 296, "joke": "Chuck Norris uses 8'x10' sheets of plywood as toilet paper.", "categories": [] },
    { "id": 184, "joke": "If at first you don't succeed, you're not Chuck Norris.", "categories": [] },
    { "id": 212, "joke": "Chuck Norris does not play the lottery. It doesn't have nearly enough balls.", "categories": [] },
    { "id": 619, "joke": "Chuck Norris can lock a safe and keep the key inside it.", "categories": [] }
  ]
}

Enter fullscreen mode Exit fullscreen mode

First, In our js/script.js we’d handle fading in and out of the preloader and toggling the FAB.

(function ($) {
  jQuery(window).on('load', function(){
    jQuery("#preloader").fadeOut(4000);
  });

  $('.fixed-action-btn').floatingActionButton({
    toolbarEnabled: true
  });

  $(document).ready(function(){
    $('.modal').modal();
  });
}(jQuery));

Enter fullscreen mode Exit fullscreen mode

Next, we call the getData and showTip functions once the page is done loading:

document.addEventListener("DOMContentLoaded", function () {
  getData();
  showTip();
});

Enter fullscreen mode Exit fullscreen mode

Let’s implement the getData function:

Using the Javascript fetch function, we’ll send a GET request to the endpoint, get the JSON response, and set it as the value of the “jokes” key of the context object. We’ll also grab the content of the script element and compile it into a template so it can be executed. Finally, we render the compiled template:

function getData() {
  fetch("https://api.icndb.com/jokes/random/10")
  .then(res => res.json()).then(data =>  {

    if (data.type == "success") {

      let source = document.getElementById("entry-template").innerHTML;
      let template = Handlebars.compile(source);

      var context = {
        jokes: data.value
      }

      let html = template(context);
      $('#render_here').html(html);

    } else if (!data || (response.status != 200)) {
      $('#failure').show();
    }
  });
}

Enter fullscreen mode Exit fullscreen mode

Next, we implement the showTip function:

function showTip() {
  $('.tap-target').tapTarget();
  $('.tap-target').tapTarget('open');
}

Enter fullscreen mode Exit fullscreen mode

Finally, we write a function to reload the jokes and another to react to a joke:

function reloadJokes() {
  getData();
  window.scrollTo(0, 0);
}

Enter fullscreen mode Exit fullscreen mode

To react to a joke:

function lmfao(_id) {
  // Cache awesome stuff here.
  $(`#${_id}`).addClass('disabled');
  M.toast({html: 'Fucking hillarious!'});
}

Enter fullscreen mode Exit fullscreen mode

If you’ve come this far, I’d expect you now understand the idea behind Jamstack and have successfully replicated Chucklarious. 🎉🎉🎉

Be sure to read more on the Jamstack ecosystem and get your hands dirty with practice.

You can find a live example here. The source code also lives here.

GitHub logo MartinsOnuoha / chuck-norris

Random Chuck Norris Jokes in JAMSTACK

Top comments (0)