The goal

Create a simple site, which has categories and subcategories, with NUXT JS and a REST api, and then deploy it on a remote host.

Knowledge to have

If you want to get started here, you should learn some things before:

Vue.js -> With vue.js we can easily build reuseable web components

NUXT -> nuxt.js builts a server side rendering functionality on top of vue.js

json-server -> creates a simple and powerful json rest api

Requirements

You should have following stack:

ssh access to your production environment

nodejs >= 9

pm2 -> install with npm install -g pm2

yarn -> you can install it with: npm install -g yarn

a running apache environment (for proxying the nodejs instances)

Basic setup

create a new folder and create your package.json in the terminal with yarn init Answer the few questions to your project (or just hit enter a few times :) )

Dependencies

We need some vendor packages, install them with:

yarn add nuxt yarn add @nuxtjs/axios yarn add @nuxtjs/markdownit yarn add dotenv yarn add json-server yarn add markdown-it-highlightjs yarn add node-sass yarn add sass-loader

or the shorthand

yarn add nuxt @nuxtjs/axios @nuxtjs/markdownit dotenv json-server markdown-it-highlightjs node-sass sass-loader

after installing the dependencies, start configuring your package.json.

package.json update

Open your package.json file.

We are creating 4 tasks, we can start afterwards:

"scripts" : { "dev" : "nuxt" , "build" : "nuxt build" , "start" : "nuxt start" , "api" : "json-server --watch api/db.json --read-only" },

After the edit, your package.json file should look like:

{ "name" : "your-project-name" , "version" : "1.0.0" , "main" : "index.js" , "license" : "MIT" , "scripts" : { "dev" : "nuxt" , "build" : "nuxt build" , "start" : "nuxt start" , "api" : "json-server --watch api/db.json --read-only" }, "dependencies" : { "@nuxtjs/axios" : "^5.3.1" , "@nuxtjs/markdownit" : "^1.2.1" , "dotenv" : "^5.0.1" , "highlight.js" : "^9.12.0" , "json-server" : "^0.12.2" , "markdown-it-highlightjs" : "^3.0.0" , "node-sass" : "^4.9.0" , "nuxt" : "^1.4.0" , "sass-loader" : "^7.0.1" } }

Project tasks

These 4 tasks we just created, have following functionality

task function yarn run dev This is our task, while we are developing. Everytime we change a file, the project will automatically recompiled yarn run build After developing, when we deploy, start this task to compile and optimize the web package yarn run start This task starts a production node server yarn run api This tasks starts the json-server which works as REST API for your page

API setup

create a new folder in your project root named api and create a new json file with the name: db.json

edit this file and create a simple structure like:

{ "categories" : [ { "id" : 1 , "title" : "My Category" , "slug" : "my-category" , "content" : "my-category.md" , "meta" : { "keywords" : "" , "description" : "" } } ], "subcategories" : [ { "id" : 1 , "categoryId" : 1 , "title" : "My Subcategory" , "slug" : "my-subcategory" , "content" : "my-category/my-subcategory.md" , "meta" : { "keywords" : "" , "description" : "" } } ] }

category fields

field description id the current id title the title slug the current url segment content the markdown file path (relative to the contents folder we create later on) meta the current meta datas (keywords and description) we need for SEO

subcategory fields

field description id the current id categoryId the category id of the current subcategory title the title slug the current url segment content the markdown file path (relative to the contents folder we create later on) meta the current meta datas (keywords and description) we need for SEO

Now we can run the yarn task: yarn run api and you should see the documentation of the current api at http://localhost:3001

NUXT config

create a new file on your project root folder named nuxt.config.js

require ( 'dotenv' ).config(); module .exports = { head: { titleTemplate : '%s - ' + process.env.SITE_NAME, meta: [ { charset : 'utf-8' }, { name : 'viewport' , content : 'width=device-width, initial-scale=1' }, { name : 'HandheldFriendly' , content : 'true' } ] }, css : [ '~/assets/scss/styles.scss' , { src : '~/node_modules/highlight.js/styles/atom-one-dark.css' , lang : 'css' } ], plugins : [ '~/plugins/components' ], modules : [ '@nuxtjs/axios' , '@nuxtjs/markdownit' ], build : { extractCSS : true , }, markdownit: { preset : 'default' , linkify : true , breaks : true , use : [ 'markdown-it-highlightjs' ] } }

As you see, we need a few more files:

file description /plugins/components.js in this file, we will inject our vue components in all page instances /assets/scss/styles.scss this files is the entry file for your (S)CSS

The /plugins/components.js has following syntax

import Vue from 'vue' import YourComponent from '~/components/YourComponent' Vue.component( 'your-component' , YourComponent)

So every component you create has to be imported and registered as a global vue component.

the .env file

create a new file at /.env

and enter following content:

API_URL =http://localhost: 3001 SITE_NAME =My Site

Since we have our api running at port 3001, we have to tell this info our site with the parameter API_URL

Our first page

lets create a new file at /pages/index.vue . This will create our first page.

This file has following content:

< template > < div > Hello Nuxt </ div > </ template >

Save the file and run yarn run dev in the terminal. After the compiling task has finished, you can go to http://localhost:3000 and you should see the text: "Hello Nuxt"

Lets get the categories

We need to make some changes to our pages/index.vue file

< template > < div > < div v-for = "category in categories" :key = "`category-link-${ category.slug }`" > < a :href = "category.slug" > {{ category.title }} </ a > </ div > </ div > </ template > < script > export default { async asyncData({ app }){ let categories = await app.$axios.$get( 'categories' ); return { categories } } } </ script >

What we have done

We fetched the categories from our api and stored them in our component data holder.

In the template itself, we loop through the categories and create links for them

the category page

to view the category content, we need to create a new vue page at ``` /pages/_category/index.vue

<template> <div> I am a category </div> </template>

After you saved this file, you can click on the "My category" link at the website, and you should see "I am a category"

Next, we want to show the category content. So create a new markdown file at /contents/my-category.md

# My category content this is the content of my category

Now we can head back to the file /pages/_category/index.vue

< template > < div > < h1 > {{ category.title }} </ h1 > < div v-html = "content" > </ div > < div > < h2 > Subcategories </ h2 > < div v-for = "subcategory in category.subcategories" :key = "`subcategory-link-${ subcategory.slug }`" > < a :href = "`/${ category.slug }/${ subcategory.slug }`" > {{ subcategory.title }} </ a > </ div > </ div > < div > < h2 > All categories </ h2 > < div v-for = "category in categories" :key = "`category-link-${ category.slug }`" > < a :href = "category.slug" > {{ category.title }} </ a > </ div > </ div > </ div > </ template > < script > export default { head(){ return { title : this .category.title, meta : [ { name : 'description' , content : this .category.meta.description } ] } }, async asyncData({app, params }){ let categories = await app.$axios.$get( 'categories' ); let category = await app.$axios.$get( `categories?slug= ${ params.category } &_embed=subcategories` ); category = category[ 0 ]; let content = require ( `~/contents/ ${ category.content } ` ); return { categories, category, content } } } </ script >

What we have done

We fetched the all categories and the current category from the api, then we fetched the markdown content from the contents folder.

In the template, we show the category title, and the content, after that, we show all subcategories and all categories.

the subcategory page

The same way, we created the category page, we create the subcategory page in /pages/_category/_subcategory/index.vue

Try to create the subcategory page by yourself. You'll find the solution at github

Apache vhost configuration

Because we don't want to give the users a strange url like http://your.domain.name:3000 we are proxying the yarn task

You'll need the following configuration at your virtualhost

<VirtualHost *:80> ServerName your.domain.name ProxyRequests on ProxyPass / http://localhost:3000/ ProxyPassReverse / http://localhost:3000/ </VirtualHost>

and for the api, you'll need to do the same.

<VirtualHost *:80> ServerName api.your.domain.name ProxyRequests on ProxyPass / http://localhost:3001/ ProxyPassReverse / http://localhost:3001/ </VirtualHost>

Start the production system

now we need to start the api and the site.

Before we can do it, we need to compile the current nuxt project with yarn run build

Then start the api with

pm2 start --name= "my-api" yarn -- run api

After that start the site with

pm2 start --name= "my-site" yarn -- run start

Let pm2 remember your tasks with pm2 save

Resume

Now we have a working website system, in which we can add contents by simply add contents in the db.json file and the markdown files. Of course, you'll need to secure your api endpoint, the json-server package is just for api mockups. There is a nice authorization example here

The github repo of this tutorial is here: https://github.com/daspete/basic-nuxt-rest