Create a simple site, which has categories and subcategories, with NUXT JS and a REST api, and then deploy it on a remote host.
If you want to get started here, you should learn some things before:
You should have following stack:
npm install -g pm2
npm install -g yarn
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 :) )
We need some vendor packages, install them with:
yarn add nuxt # the nuxtjs package
yarn add @nuxtjs/axios # the XHR package for nuxt
yarn add @nuxtjs/markdownit # package to give you the ability to write markdown
yarn add dotenv # helper to use a .env file for different configurations per hosting
yarn add json-server # a simple REST api, organized with json data
yarn add markdown-it-highlightjs # The loader for highlight.js
yarn add node-sass # if you want to use sass in your project
yarn add sass-loader # let nuxt load sass-files
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.
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"
}
}
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 |
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": ""
}
}
]
}
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 |
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
create a new file on your project root folder named nuxt.config.js
// load our .env file
require('dotenv').config();
module.exports = {
// setting the currents page html header params
head: {
titleTemplate: '%s - ' + process.env.SITE_NAME, // the title of the site, %s is the placeholder for the current page's title
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ name: 'HandheldFriendly', content: 'true' }
]
},
css: [
'~/assets/scss/styles.scss', // load the current project scss entry file
{ src: '~/node_modules/highlight.js/styles/atom-one-dark.css', lang: 'css' } // use the atom-one-dark theme for highlight.js
],
plugins: [
'~/plugins/components' // inject all page components
],
modules: [
'@nuxtjs/axios', // XHR package
'@nuxtjs/markdownit' // compile markdown files
],
build: {
extractCSS: true, // extract inline css into separate files
},
// the markdownit configuration
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.js
import Vue from 'vue'
// import your components
import YourComponent from '~/components/YourComponent'
// register your components
Vue.component('your-component', YourComponent)
So every component you create has to be imported and registered as a global vue component.
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
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"
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 {
// since we use an ajax call to our api, we need to wait for the data
// to have an asynchrous function, we need to make it async
async asyncData({ app }){
// let's get all categories from our api
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
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 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
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>
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
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