Pre configuration after generating Nuxt project

Babel Config

For the all annoying error messages that doesn’t make sense, when you use Webpack Babel and ES6 with Node require.

When you use module.export and return as object whether be {} or () => ({}) which is actually valid, but Webpack likes to choke on something insignificant.

To solve this config your Babel to:

{
    sourceType: 'unambiguous'
}

In this case with Nuxt:

module.export = {
    build: {
        babel: {
            sourceType: 'unambiguous'
        }
    }
}
Snippet: nuxt.config.js

Visual Studio Code Settings

{
  "prettier.arrowParens": "avoid",
  "prettier.bracketSpacing": true,
  "prettier.jsxBracketSameLine": true,
  "prettier.jsxSingleQuote": true,
  "prettier.semi": false,
  "prettier.singleQuote": true,
  "prettier.tabWidth": 2,
  "prettier.trailingComma": "none",
  "prettier.useTabs": true,
  "prettier.quoteProps": "preserve",
  "vetur.format.enable": true,
  "vetur.format.options.useTabs": true,
  "vetur.format.options.tabSize": 2,
  "vetur.format.scriptInitialIndent": false,
  "vetur.format.styleInitialIndent": false,
  // "vetur.format.defaultFormatter.js": "prettier",
  "vetur.format.defaultFormatterOptions": {
    "prettier": {
      // Prettier option here
      "arrowParens": "avoid",
      "bracketSpacing": true,
      "jsxBracketSameLine": true,
      "jsxSingleQuote": true,
      "semi": false,
      "singleQuote": true,
      "tabWidth": 2,
      "trailingComma": "none",
      "useTabs": true
    }
  },
  "vetur.validation.script": true,
  "javascript.format.semicolons": "remove",
  "beautify.language": {
    "js": {
      "type": [
        "javascript",
        "json",
        "jsonc"
      ],
      "filename": [
        ".jshintrc",
        ".jsbeautifyrc"
      ],
      "indent_size": 2,
      "indent_char": " ",
      "indent_level": 0,
      "indent_with_tabs": true,
      "preserve_newlines": true,
      "max_preserve_newlines": 10,
      "jslint_happy": false,
      "space_after_named_function": false,
      "space_after_anon_function": false,
      "brace_style": "collapse",
      "keep_array_indentation": false,
      "keep_function_indentation": false,
      "space_before_conditional": true,
      "break_chained_methods": false,
      "eval_code": false,
      "unescape_strings": false,
      "wrap_line_length": 0,
      "indent_empty_lines": false
    },
    "css": [
      "css",
      "less",
      "scss"
    ],
    "html": [
      "htm",
      "html"
    ]
  },
  "editor.formatOnSave": true
}
File: /.vscode/settings.json
Personal Configuration
There are still annoyances when it’s about auto-parenthesis on condition operators, which is bullshit. And I don’t find a way to turn it off.
Example:
 valid:
    A && B || C
    A * B + C

 turn into:
    (A && B) || C
    (A * B) + C

Learn maths and don’t be confused about it.

.prettierrc

If that doesn’t work create .prettierrc

{
    "arrowParens": "avoid",
    "bracketSpacing": true,
    "jsxBracketSameLine": true,
    "jsxSingleQuote": true,
    "semi": false,
    "singleQuote": true,
    "tabWidth": 2,
    "trailingComma": "none",
    "useTabs": true
}
File: .prettierrc

Global Vue Directives

/lib/directives/
  index.js
/plugins/
  directives.js
nuxt.config.js
File Structure

// https://webpack.js.org/guides/dependency-management/#require-context
const requireComponent = require.context(
	// Look for files in the current directory
	'.',
	// Do not look in subdirectories
	false,
	// Only include "v-" prefixed .js files
	/v-[\w-]+\.js$/
)

const directives = {}
// For each matching file name...
requireComponent.keys().forEach(fileName => {
	// Get the component config
	const componentConfig = requireComponent(fileName)
	// Get the PascalCase version of the component name
	const componentName = fileName
		// Remove the "./_v-" from the beginning
		.replace(/^\.\/v-/, '')
		// Remove the file extension from the end
		.replace(/\.\w+$/, '')

	// Globally register the component
	directives[componentName] = componentConfig.default || componentConfig
})

export default directives
File: /lib/directives/index.js
import Vue from 'vue'
import directives from '../lib/directives'
Object.entries(directives).forEach(([directiveName, fn]) =>
	Vue.directive(directiveName, fn)
)
File: /plugins/directives.js
export default {
    /*
     ** Plugins to load before mounting the App
     */
	plugins: ['~/plugins/directives']
}
Snippet: nuxt.config.js

Add Directive

Example: Auto import Vue directives from directory

Components

// Get all Vue files

// https://webpack.js.org/guides/dependency-management/#require-context
const requireComponent = require.context(
	// Look for files in the current directory
	'.',
	// Do not look in subdirectories
	false,
	// Only include all .vue files
	/\.vue$/
)

const components = {}
// For each matching file name...
requireComponent.keys().forEach(fileName => {
	// Get the component config
	const componentConfig = requireComponent(fileName)
	// Get the PascalCase version of the component name
	const componentName = fileName
		// Remove the "./_" from the beginning
		.replace(/^\.\//, '')
		// Remove the file extension from the end
		.replace(/\.\w+$/, '')

	// Globally register the component
	components[componentName] = componentConfig.default || componentConfig
})

module.exports = components
File: /components/index.js
Doesn’t look in subdirectories

Self note

You can skip this...
More shortcomings of ES6 import/export, what I hate.

I’ve tried ES6 way:

// File: /components/index.js
export default components

// File: /pages/index.vue
// JavaScript Destructuring: Logo
import { Logo } from '~/components'

// Error: "export 'Logo' was not found in '~/components'
Error

But this works?

// File: /components/index.js
export default components

// File: /pages/index.vue
import components from '~/components'
const { Logo } = components

// Why the JavaScript Destructuring work like this 
// (╯°□°)╯︵ ┻━┻ ʇɹodxǝ/ʇɹodɯᴉ 9SƎ
Why does this work?

For importing things I even tried:

import { 
  BaseButton,
  BaseIcon,
  BaseInput } as components from '~/components'

export default {
  components
}
Error - Make Destructing as alias components 
In short: There’s no dynamic way for doing things. Node’s module.exports FTW!

How to use it

/components/
  Logo.vue
  BaseButton.vue
  BaseIcon.vue
  BaseInput.vue
Example File Structure

Instead of this:

<script>
import BaseButton from '~/components/BaseButton.vue'
import BaseIcon from '~/components/BaseIcon.vue'
import BaseInput from '~/components/BaseInput.vue'

export default {
  components: {
    BaseButton,
    BaseIcon,
    BaseInput
  }
}
</script>
Common Way - Snippet: /pages/index.vue

You can do this:

<script>
import { 
  BaseButton,
  BaseIcon,
  BaseInput } from '~/components'

export default {
  components: { 
    BaseButton,
    BaseIcon,
    BaseInput 
  }
}
</script>
Slight Advanced Way - Snippet: /pages/index.vue
Not tested on TreeShaking things, need confirmation, someone can tell me please.
After writing this I’m slightly discomfort about this. This may defeat the purpose of async, but nevertheless a great example for next new ideas.

Writing a test api

Default generated without ExpressJS or Fastify
/api/
  test.js
nuxt.config.js
File Structure
export default (req, res) => {
	res.write('Hey! Test')
	res.end()
}
File: /api/test.js
module.exports = {
	serverMiddleware: [
		// Will register file from project api directory to handle /api/* requires
		{ path: '/api/test', handler: '~/api/test.js' }
		// { path: '/api', handler: '~/api/index.js' }
	]
}
Snippet: nuxt.config.js

Visite page: /api/test

When there’s lack of documentation, even for creating a test api in default way.

Nuxt Fastify (Generated with server)

When you generate Nuxt project from CLI with Fastify as server
/api/
  index.js
/server/
  index.js
  

Fastify: Api

const fs = require('fs')
const uuidv4 = require('uuid/v4')
const Jsonfile = require('../lib/Jsonfile')
const cvFile = new Jsonfile('data/cv.json', [])
const fileDatabase = require('../lib/fileDatabase'),
	Login = () => fileDatabase.collection('login', { unique: ['uuid'] }),
	Emails = () => fileDatabase.collection('emails', { unique: ['email'] })
const CryptoJS = require('crypto-js'),
	encrypt = (message, key) => CryptoJS.AES.encrypt(message, key).toString(),
	decrypt = (encryptedMessage, key) =>
		CryptoJS.AES.decrypt(encryptedMessage, key).toString(CryptoJS.enc.Utf8)
const jwt = require('../lib/jwt')
const getUuidFromToken = object => object.sub
// const readJson = require('../lib/readJson')

const routes = async (fastify, options) => {
	fastify
		.get('/cv', (request, reply) => {
			const readJson = (file, returnValueIfFail) =>
				new Promise((fulfil, reject) => {
					if (typeof fs.readFile !== 'function') throw []
					fs.readFile(file, (err, data) => {
						if (err) reject(returnValueIfFail)
						fulfil(data)
					})
				}).then(fileData => {
					try {
						return JSON.parse(fileData)
					} catch (e) {
						throw returnValueIfFail
					}
				})

			return readJson('data/cv.json', [])
				.then(reply.send.bind(reply))
				.catch(message =>
					reply.code(400).send({
						success: false,
						message
					})
				)
			// .then(cv => ({ cv }))
			// .catch(cv => ({ cv }))
		})
		.post('/cv', {}, (request, reply) => {
			const { token, data } = request.body
			return jwt
				.verify(token)
				.then(getUuidFromToken)
				.then(uuid => Login().findOne({ uuid }))
				.then(item => {
					if (!item)
						throw {
							success: false,
							name: 'Uuid not passed',
							message: 'Nothing to update'
						}

					cvFile.data = (data.hasOwnProperty('cv') && data.cv) || []
					return Promise.all([jwt.signUuid(item.uuid), cvFile.data]).then(
						values => ({
							token: values[0],
							cv: values[1]
						})
					)
				})
				.then(data => {
					console.log({ data })
					reply.code(200).send(data)
				})
				.catch(reply.code(400).send.bind(reply))
		})
		.post('/register', {}, (request, reply) => {
			const { email, password } = request.body
			const foundEmail = Emails().findOne({ email })
			if (foundEmail)
				return reply.code(406).send({
					success: false,
					message: 'There can only be one!'
				})

			const uuid = uuidv4()
			const insertedLogin = Login().insert({
				uuid,
				password: encrypt(password, uuid)
			})
			if (!insertedLogin)
				return reply.code(400).send({
					success: false,
					message: ' - insertedLogin | failed'
				})

			const insertedEmail = Emails().insert({
				uuid: insertedLogin.uuid,
				email
			})
			if (!insertedEmail)
				return reply.code(400).send({
					success: false,
					message: ' - insertedEmail | failed'
				})

			jwt
				.signUuid(insertedLogin.uuid)
				.then(reply.send.bind(reply))
				.catch(reply.send.bind(reply))
		})
		.post('/login', {}, (request, reply) => {
			const { email, password } = request.body
			const foundEmail = Emails().findOne({ email })
			if (!foundEmail)
				return reply.code(400).send({
					success: false,
					message: ' - foundEmail | Email not found'
				})

			const foundLogin = Login().findOne({ uuid: foundEmail.uuid })
			if (!foundLogin)
				return reply.code(400).send({
					success: false,
					message: ' - foundLogin | User not found'
				})

			const decryptedPassword = decrypt(foundLogin.password, foundLogin.uuid)
			if (decryptedPassword !== password)
				return reply.code(400).send({
					success: false,
					message: ' - decryptedPassword | You shall not pass!'
				})

			jwt
				.signUuid(foundLogin.uuid)
				.then(reply.send.bind(reply))
				.catch(reply.send.bind(reply))
		})
		.post('/verifytoken', {}, (request, reply) => {
			const { token } = request.body
			jwt
				.verify(token)
				.then(() => reply.code(200).send(true))
				.catch(() => reply.code(400).send(false))
		})
}

module.exports = routes
Example File: /api/index.js
 /api/cv
 /api/register
 /api/login
 /api/verifytoken

Fastify: server

const fs = require('fs-extra')
const setOwnerWwwData = require('./setOwnerWwwData')

const { Nuxt, Builder } = require('nuxt')
const fastify = require('fastify')({
	logger: true
})

// Import and Set Nuxt.js options
const config = require('../nuxt.config.js')
config.dev = process.env.NODE_ENV !== 'production'
if (config.server.port && isNaN(config.server.port))
	fs.removeSync(config.server.port)
// setOwnerWwwData()

async function start() {
	// Instantiate nuxt.js
	const nuxt = new Nuxt(config)

	const {
		host = process.env.HOST || '127.0.0.1',
		port = process.env.PORT || 3000
	} = nuxt.options.server

	// Build only in dev mode
	if (config.dev) {
		const builder = new Builder(nuxt)
		await builder.build()
	} else {
		await nuxt.ready()
	}
    
    fastify.addContentTypeParser(
		'application/json',
		{ parseAs: 'string', bodyLimit: 8e7 },
		function(req, body, done) {
			try {
				done(null, JSON.parse(Buffer.from(body).toString()))
			} catch (err) {
				err.statusCode = 400
				done(err, undefined)
			}
		}
	)

	fastify.register(require('../api'), { prefix: '/api' })

	// fastify.get('*', ({ req }, { res }) => nuxt.render(req, res))
	fastify.setNotFoundHandler(({ req }, { res }) => nuxt.render(req, res))

	fastify.listen(port, host, (err, address) => {
		if (err) {
			fastify.log.error(err)
			process.exit(1)
		}
		if (isNaN(port)) fs.chmodSync(port, 0o777)
	})
}

start()
File: /server/index.js
From the default, you need to modify the server file.
Add line: fastify.register
Add line: fastify.setNotFoundHandler
Make sure path is correct: require('../api') and use prefix: '/api'
When there is zero documentation and examples, I had to look for clues. Finally I made it work on Fastify.
When you use: Fastify or Express, serverMiddleware is useless in nuxt.config.js, but still you can use them both, if you want to.
Weird stuff happening for paths not working properly (sometimes) running Nuxt as socket file on Nginx. But somehow on proxy with port it work.
application/json 413 LIMIT fix. 10MB instead of 1 MB.

Nuxt FastifyJS (Generated without server, but added later)

When you generate Nuxt project from CLI and decide to add Fastify later
I saw an example for ExpressJS, and I finally get this to work. It’s a hack.
/api/
  routes/
    users.js
  index.js
nuxt.config.js
Template File Structure

In nuxt.config.js you point to a file that loads Fastify as an app.
That’s very different way how this generator does it. This is the working concept, files might not work correctly.

module.exports = {
  serverMiddleware: [
    // API middleware
    '~/api/index.js'
  ]
}
Snippet: nuxt.config.js
const fastify = require('fastify')({ logger: true })

// Declare a route
fastify.get('/', async () => ({ hello: 'world' }))

// Require API routes
const users = require('./routes/users')

// Import API Routes
fastify.register(users)

fastify.setNotFoundHandler((req, res) => {
	console.log('DID I JUST FUCKING RESPOND?')
	res.code(404).send({ error: 404, message: res.message })
})

// Export the server middleware
module.exports = {
	path: '/api',
	handler: (req, res) =>
		fastify
			.inject(req)
			.then(response => {
				res.writeHead(response.statusCode, { ...response.headers })
				res.write(response.rawPayload)
				res.end()
				console.log({ res, response })
			})
			.catch(console.error.bind(console, 'FAIL: appInjectReq'))
}
File: /api/index.js
// Mock Users
const users = [
    { name: 'Alexandre' }, 
    { name: 'Pooya' }, 
    { name: 'Sébastien' }]

const routes = async (fastify, options) => {
	/* GET users listing. */
	fastify.get('/users', async () => users)

	/* GET user by ID. */
	fastify.get('/users/:id', async (request, reply, next) => {
		const id = parseInt(request.params.id)
		if (id >= 0 && id < users.length) {
			return users[id]
		} else {
			reply.code(404)
			throw new Error('User not found')
		}
	})
}

module.exports = routes
File: /api/routes/users.js
UPDATE: Something wrong with content length for Posts
After experimenting, for basic things it doesn’t work. hmm.
New Update: I get things to work with utf-8 headers. Not sure there are easier way to copy Fastify response properties to Node HTTP ones. Might use the CLI generator or ExpressJS to save time.
The reponse headers information: Node HTTP

Nuxt ExpressJS (Generated without server, but added later)

Saw interesting example about this template: https://github.com/nuxt-community/express-template/blob/master/template/api/index.js
/api/
  routes/
    users.js
  index.js
nuxt.config.js
Template File Structure

In nuxt.config.js you point to a file that loads Express as an app.
That’s very different way how this generator does it. This is the concept, files might not work correctly.

module.exports = {
  serverMiddleware: [
    // API middleware
    '~/api/index.js'
  ]
}
Snippet: nuxt.config.js
const express = require('express')

// Create express instance
const app = express()

// Require API routes
const users = require('./routes/users')

// Import API Routes
app.use(users)

// Export the server middleware
module.exports = {
  path: '/api',
  handler: app
}
File: /api/index.js
const { Router } = require('express')

const router = Router()

// Mock Users
const users = [
  { name: 'Alexandre' },
  { name: 'Pooya' },
  { name: 'Sébastien' }
]

/* GET users listing. */
router.get('/users', function (req, res, next) {
  res.json(users)
})

/* GET user by ID. */
router.get('/users/:id', function (req, res, next) {
  const id = parseInt(req.params.id)
  if (id >= 0 && id < users.length) {
    res.json(users[id])
  } else {
    res.sendStatus(404)
  }
})

module.exports = router
File: /api/routes/users.js
I can confirm for this Express Template concept it works!

I had this eureka moment, almost same file structuring what I’m used to do ExpressJS, but instead the root, you do for example in /api.

I’m going to try this concept for Fastify.

Nuxt custom layout: edit

/layouts/
  edit.vue
/pages/cv/
  index.vue
Example File Structure
<template>
	<edit>
		<nuxt />
	</edit>
</template>

<script>
import edit from '~/components/layout/edit'

export default {
	name: 'layout-edit',
	components: {
		edit
	}
}
</script> 
File: /layouts/edit.vue
export default {
    layout: 'edit'
}
Snippet: When you use the layout edit the file
<template>
	<view-cv :cv="cv" />
</template>


<script>
import viewCv from '~/components/cv/view/index'
import { state, mutations, actions } from '~/lib/store/_store-cv'

export default {
	name: 'page-cv',
	layout: 'edit',
	components: {
		viewCv
	},
	computed: {
		cv() {
			return state.list
		}
	},
	beforeCreate() {
		return actions.onGet()
	}
}
</script>
File: /pages/cv/index.vue

Watch more directories and restart NUXT

When you have api folder, the server doesn’t restart automatically during development.

To watch these folder you have to add extra parameters --watch api.

Like so: cross-env NODE_ENV=development nodemon server/index.js --watch server --watch api --watch lib

{

	"scripts": {
		"dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server --watch api --watch lib"
	}
}
Snippet: package.json

Docker

FROM node:13.6-alpine

EXPOSE 3000

ENV NODE_ENV=production
ENV PORT=3000
ENV NODE_WORKDIR=/app
WORKDIR $NODE_WORKDIR

COPY package.json $NODE_WORKDIR/
RUN npm shrinkwrap
RUN npm i
COPY . $NODE_WORKDIR

CMD ["npm", "run", "docker"]
File: Dockerfile
module.exports = {
    server: {
        host: '0.0.0.0'
    }
}
Snippet: nuxt.config.js
{
	"scripts": {
		"docker": "cross-env NODE_ENV=production node server/index",
		"docker-build": "docker build -t harianto/nuxt-app .",
		"docker-push": "nuxt build && docker build -t harianto/nuxt-app . && docker push harianto/nuxt-app"
	}
}
Snippet: package.json