I'm Jonas Galvez, a JavaScript and Go engineer at STORED e-commerce. I started my carreer as an ActionScript developer 18 years ago. Since then, I've had a 10-year long affair with Python and have now returned to ECMAScript land.

I'm an eternal student of distributed systems and practitioner of minimalism.

I build and deploy my stuff with Vue, Nuxt, Go and Kubernetes nowadays.

Check out my dedicated page to my professional influences.

I am driven by purpose and cultivate a stoic attitude towards life.

My Nuxt and Koa Boilerplate

Originally intended as a quick onboarding guide to new JavaScript developers in my day job, this is a guide to my preferred starter template, or boilerplate, for new Nuxt-based web applications.

Why Koa and not Express

Most people are probably unaware that Koa and Express were written by the same authors (TJ Holowaychuk et al). Koa takes on a different, minimalist approach, restraining itself to around 2k LOC and focusing on enabling DIY middleware solutions. I won't go into detail on why you should consider Koa instead of Express because honestly, I don't think it makes a dramatic difference, but for my projects and my team, it has definitely not been a regrettable choice.

Using Koa does equal a little bit more work, as it requires you to put together some server functions yourself. For instance, Express has a built-in HTTP body parser, whereas in my Koa setup, I need to have a middleware defined as follows:</p>

// Parses HTTP POST data (including uploads)
// and automatically parses any sent JSON
app.use(async (ctx, next) => {
  if (['GET', 'DELETE'].includes(ctx.request.method)) {
    await next()
  } else if (ctx.is('application/json')) {
    ctx.json = await parse.json(ctx)
    await next()
  } else if (ctx.is('multipart/*') === 'multipart/form-data') {
    ctx.parts = multipartParse(ctx, { autoFields: true })
    await next()
  } else {
    try {
      ctx.json = await parse.json(ctx)
    } catch (err) {
      ctx.json = null
    }
    await next()
  }
})

That said, I prefer having a thin server and the ability to add complexity as needed. So the above snippet is one of a small set of additions to a base Koa server that I tend to use on every project. You can find it commented on my boilerplate repo.

API request routing

If you have data-only API functions that are used in client requests, you probably want to have them executed and returning before reaching the Nuxt renderer. Not that you couldn't do well with Nuxt's own middleware setup, asyncData and nuxtServerInit, but it just makes a lot more sense to have Koa handle data-only requests directly.

For this I use a middleware that will translate requests like /api/service/method into an actual Service.method() call on the server. The following middleware routes such requests to methods in a services.js module. This can and probably should be tweaked to match your application's API needs:

app.use(async (ctx, next) => {
  if (ctx.path.startsWith('/api') === false || ctx.request.method !== 'POST') {
    await next()
  } else {
    const apiMethod = ctx.path.split('/api/')[1]
    let [service, method] = apiMethod.split('/')
    service = translatePath(service)
      .replace(/^(.)(.*)/, (_, f, r) => `${f.toUpperCase()}${r}`)
    method = translatePath(method)
    ctx.body = await services[service][method](ctx.json)
  }
})

In ContaGrama, a project of mine that makes use of this boilerplate, it made sense to call this file models.js. There you can see a more complex example, with relating calls in the pages/index.vue, pages/foods/index.vue and pages/foods/_id.vue.

ElementUI setup

Over the past couple of years, I've worked on three ElementUI-based applications. ElementUI has now become a permanent addition to my stack, as it provides a rich set of elements and rather idiomatic Vue implementation. In order to make it work in Nuxt, you need to include element-theme-default's stylesheets and define a Nuxt plugin to instantiate it:

import Vue from 'vue'

const ElementUI = require('element-ui')
const locale = require('element-ui/lib/locale/lang/en')
Vue.use(ElementUI, { locale })

ESLint

I can't live without ESLint anymore. Ideally, you'll want to have npm run lint automatically execute before any push. In my setup I've configured it to run automatically in Nuxt's development mode.

My ESLint is also configured to use StandardJS, so things like semicolons, or parenthesis in a function or method definition with no preceding empty space will cause a linting error. It took me a while to get used to this formatting style, but I can see it's value after so much time. FWIW, Brendan Eich endorsed StandardJS, stating that it enforces safe & cleaner low-semicolon style.

Get it from https://github.com/galvez/boilerplates.