Shipping Jamstack like a hero - beware, it's cool

gempain

Geoffroy Empain

Posted on January 25, 2021

Shipping Jamstack like a hero - beware, it's cool

So you love React, Angular, Vue, Gatsby and other Jamstack frameworks ? Hooray, I do too πŸŽ‰

But.... we've all had these issues:

  • How do I deploy ? With Netlify, Docker, or a good old HTTP server ? πŸ€”
  • When I need to change my API URL... I have to rebuild and re-deploy 😞
  • When I need to preview a small change... re-deploying takes forever 😞
  • Managing SSL certificates is a pain. It should be automatic when I ship a new site 😑

The solution: Meli, a self-hosted platform built on top of the well-known Caddy Server. Out of the box, you get automatic HTTPs, zero-downtime, and heavy-duty performance.

I've installed Meli on my VPS, so I'll skip this part which takes about 2 minutes with Docker Compose, but checkout the docs for instructions. From here, I'll assume you've installed Meli at https://meli.company.com.

Meli login scren

Deploying a site to Meli

Let's start with a very simple site, dist/index.html

<!doctype html>
<html lang="en">
<head>
    <title>Meli example Vue.js app</title>
</head>
<body>
Hello !
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Once logged in to your Meli instance:

  1. create a site named hello Create a site in Meli
  2. get your site ID Get your site ID in Meli
  3. get your upload token Get your site upload token in Meli
  4. upload your site
npx @getmeli/cli upload \
   ./dist \
    --url http://localhost:80 \
    --site "8f30f74f-4b63-4dc3-b8dc-788ca43740a8" \
    --token <site-token> \
    --branch "latest"
Enter fullscreen mode Exit fullscreen mode

Your site is now available at https://hello.meli.company.com πŸš€

Example site in meli

Serve your site at https://hello.com

Okay, I'll agree, https://hello.meli.company.com isn't really sexy. We want our awesome site to be served at https://hello.com. To do this:

  1. In your Meli site, configure a custom domain with hello.com Configure custom domain
  2. In the DNS zone of hello.com, add an A record that points the IP of meli.company.com. For subdomains like sub.hello.com, you can use a CNAME record that points to hello.meli.domain.com only when it is the single record (all types combined) configured for that subdomain.
  3. Browse https://hello.com, and off you go !

Deploying to Meli from your CI

You can also automatically deploy when you push to your Git repository. For example, with Github Actions:

name: main
on: [ push ]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v1
        with:
          node-version: '12'
      - name: "publish"
        run: |
          npx @getmeli/cli@next upload \
            ./dist \
            --url "https://meli.domain.com" \
            --site "<your-site-id>" \
            --token "$MELI_TOKEN"
        env:
          MELI_TOKEN: ${{ secrets.MELI_TOKEN }}
          # using default GITHUB_TOKEN set by Github Actions
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Enter fullscreen mode Exit fullscreen mode

Note that --branch is not used here, it is auto-detected by the Meli CLI.

With this setup, you'll get pull request previews in Github:
Screen Shot 2021-01-25 at 15.40.50

Runtime environment variables in your static site

Meli allows you to override any path in your site with dynamically generated content, per branch.

Let's see how this works with a basic Vue app. We'll replace our dist/index.html and upload it to our hello:

<!doctype html>
<html lang="en">
<head>
    <title>Meli example Vue.js app</title>
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</head>
<body>

<div id="app">
    <div v-if="loading">Loading...</div>
    <div v-if="error">{{ error }}</div>
    <div v-if="env">
        <h2>{{ env.name }}</h2>
        <pre><code>{{JSON.stringify(env, null, 2)}}</code></pre>
    </div>
</div>

<script>
  const app = new Vue({
    el: '#app',
    data: {
      loading: true,
      error: undefined,
      env: undefined,
    },
    created: function () {
      this.loading = true;
      fetch('/env.json')
        .then(res => res.json())
        .then(env => this.env = env)
        .catch(err => this.error = err)
        .finally(() => this.loading = false);
    },
  });
</script>

</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Notice that we're fetching /env.json. We'll configure this file in Meli using the UI. For now, let's upload our site to our latest branch.

npx @getmeli/cli upload \
   ./dist \
    --url http://localhost:80 \
    --site "8f30f74f-4b63-4dc3-b8dc-788ca43740a8" \
    --token <site-token> \
    --branch "latest"
Enter fullscreen mode Exit fullscreen mode

This branch will be available at https://hello.meli.domain.com. Now let's upload it to a new branch named demo:

npx @getmeli/cli upload \
   ./dist \
    --url http://localhost:80 \
    --site "8f30f74f-4b63-4dc3-b8dc-788ca43740a8" \
    --token <site-token> \
    --branch "demo"
Enter fullscreen mode Exit fullscreen mode

This branch will be served at https://demo.hello.meli.domain.com.

Now, let's configure /env.json in Meli:

  1. under branch latest, add a file redirect for path /env.json and with content {"name": "prod"} Add file redirect to latest branch
  2. in under branch demo, add a file redirect for path /env.json and with content {"name": "demo"} Create env redirect for demo branch

Now, when you go to https://hello.meli.domain.com, you see this:
Environment preview of main branch in Meli demo site
and https://demo.hello.meli.domain.com shows:
Screen Shot 2021-01-25 at 14.45.24

Wrap up

Meli is a really cool project which allows you to self-host your static sites and frontend apps. It features a lot of cool things, and more are to come.

Further reading:

πŸ’– πŸ’ͺ πŸ™… 🚩
gempain
Geoffroy Empain

Posted on January 25, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related