Authentication in a Nuxt.js App

Authentication in a Nuxt.js App

Authenticated routes example with Nuxt.js.

What we’ll be building

Below is a quick demo of what we’ll be building in this tutorial.

Table of Contents

  • What we’ll be building
  • Spinning up a quick API
  • Creating a Nuxt.js app
  • Installing necessary Nuxt.js modules
  • Creating a Navbar component
  • User registration
  • Showing whether a user is logged in or not
  • User log in
  • Displaying the user profile
  • Logging user out
  • Restricting the profile page to only logged in users
  • Creating a guest middleware
  • Conclusion

Spinning up a quick API

To save ourselves some time, we’ll clone an API, which I have put together for this tutorial:

$ git clone https://github.com/ammezie/jwt-auth-api.git

Then we install the API dependencies:

$ cd jwt-auth-api
$ npm install

Next, rename .env.example to .env and generate an APP_KEY:

// with the adonis CLI
$ adonis key:generate

// without the adonis CLI
$ node ace key:generate

Once that’s done, let’s run the migrations:

// with the adonis CLI
$ adonis migration:run

// without the adonis CLI
$ node ace migration:run

Before we move on to building the Nuxt.js app, let’s quickly go over the API. The API is built using AdonisJs and it uses JWT (JSON Web Tokens) for authentication. It also uses SQLite.

The API has three endpoints:

  • /register: endpoint for users registration.
  • /login: endpoint for authenticating users.
  • /me: endpoint for getting details for the currently authenticated user and it is protected by an auth middleware, which means a user must be authenticated to access the endpoint.

The API also already has CORS enabled.

Now, we can start the API:

$ npm start

We should be able to access the API on http://127.0.0.1:3333/api. We’ll leave it running.

Note: Though I’ll be using an API built with AdonisJs for the purpose of this tutorial, you are free to use whatever framework that work best for you.
Creating a Nuxt.js app

For this, we’ll make use of the Vue CLI, so you need to first install the Vue CLI in case you don’t have it installed already:

$ npm install -g vue-cli

Then we can create a Nuxt.js app:

$ vue init nuxt/starter nuxt-auth

Next, we need to install the dependencies:

$ cd nuxt-auth
$ npm install

We can launch the app:

$ npm run dev

The app should be running on http://localhost:3000.

Installing necessary Nuxt.js modules

Now, let’s install the Nuxt.js modules that we’ll be needing for our app. We’ll be using the Nuxt Auth module and the Nuxt Axios module, since auth module makes use of Axios internally:

$ npm install @nuxtjs/auth @nuxtjs/axios --save

Once that’s done, add the code below to nuxt.config.js:

// nuxt.config.js

modules: [
'@nuxtjs/axios',
'@nuxtjs/auth'
],

Next, we need to set up the modules. Paste the code below into nuxt.config.js

// nuxt.config.js

axios: {
baseURL: 'http://127.0.0.1:3333/api'
},

auth: {
strategies: {
local: {
endpoints: {
login: { url: 'login', method: 'post', propertyName: 'data.token' },
user: { url: 'me', method: 'get', propertyName: 'data' },
logout: false
}
}
}
}

Here, we set the base URL (which is that of our API from earlier on) that Axios will use when making requests. Then we define the authentication endpoints for the local strategy corresponding to those on our API. On successful authentication, the token will be available in the response as a token object inside a data object, hence why we set propertyName to data.token. Similarly, the response from the /me endpoint will be inside a data object. Lastly, we set logout to falsesince our API doesn’t have an endpoint for logout. We’ll just remove the token from localstorage when a user logs out.

Creating a Navbar component

To style our app, we’ll be making use of Bulma. Open nuxt.config.js and paste the code below within the link object that is inside the head object:

// nuxt.config.js

{
rel: 'stylesheet', href: 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.1/css/bulma.min.css'
}

Now, let’s create the Navbar component. Rename AppLogo.vue inside the components directory to Navbar.vue and replace it content with the following:

// components/Navbar.vue

<template>
<nav class="navbar is-light">
<div class="container">
<div class="navbar-brand">
<nuxt-link class="navbar-item" to="/">Nuxt Auth</nuxt-link>
<button class="button navbar-burger">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div class="navbar-menu">
<div class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable">
<a class="navbar-link">
My Account
</a>
<div class="navbar-dropdown">
<nuxt-link class="navbar-item" to="/profile">My Profile</nuxt-link>
<hr class="navbar-divider">
<a class="navbar-item">Logout</a>
</div>
</div>
<nuxt-link class="navbar-item" to="/register">Register</nuxt-link>
<nuxt-link class="navbar-item" to="/login">Log In</nuxt-link>
</div>
</div>
</div>
</nav>
</template>

The Navbar component contains links to login or register, links to view profile or logout.

Next, let’s update the default layout to make use of the Navbar component. Open layouts/default.vue and replace it content with the following:

// layouts/default.vue

<template>
<div>
<Navbar/>
<nuxt/>
</div>
</template>

<script>
import Navbar from '~/components/Navbar'

export default {
components: {
Navbar
}
}
</script>

Also, let’s update the homepage. Open pages/index.vue and replace it content with the following:

// pages/index.vue

<template>
<section class="section">
<div class="container">
<h1 class="title">Nuxt Auth</h1>
</div>
</section>
</template>

Our app should now look something similar to below:

User registration

Inside the pages directory, create a new register.vue file and paste the code below in it:

// pages/register.vue

<template>
<section class="section">
<div class="container">
<div class="columns">
<div class="column is-4 is-offset-4">
<h2 class="title has-text-centered">Register!</h2>

      &lt;Notification :message="error" v-if="error"/&gt;

      &lt;form method="post" @submit.prevent="register"&gt;
        &lt;div class="field"&gt;
          &lt;label class="label"&gt;Username&lt;/label&gt;
          &lt;div class="control"&gt;
            &lt;input
              type="text"
              class="input"
              name="username"
              v-model="username"
              required
            &gt;
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="field"&gt;
          &lt;label class="label"&gt;Email&lt;/label&gt;
          &lt;div class="control"&gt;
            &lt;input
              type="email"
              class="input"
              name="email"
              v-model="email"
              required
            &gt;
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="field"&gt;
          &lt;label class="label"&gt;Password&lt;/label&gt;
          &lt;div class="control"&gt;
            &lt;input
              type="password"
              class="input"
              name="password"
              v-model="password"
              required
            &gt;
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="control"&gt;
          &lt;button type="submit" class="button is-dark is-fullwidth"&gt;Register&lt;/button&gt;
        &lt;/div&gt;
      &lt;/form&gt;

      &lt;div class="has-text-centered" style="margin-top: 20px"&gt;
        Already got an account? &lt;nuxt-link to="/login"&gt;Login&lt;/nuxt-link&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

</section>
</template>

<script>
import Notification from '~/components/Notification'

export default {
components: {
Notification,
},

data() {
return {
username: '',
email: '',
password: '',
error: null
}
},

methods: {
async register() {
try {
await this.$axios.post('register', {
username: this.username,
email: this.email,
password: this.password
})

    await this.$auth.loginWith('local', {
      data: {
        email: this.email,
        password: this.password
      },
    })

    this.$router.push('/')
  } catch (e) {
    this.error = e.response.data.message
  }
}

}
}
</script>

This contains a form with three fields: username, email and password. Each field is bind to a corresponding data on the component. When the form is submitted, a register method will be called. Using the Axios module, we make a post request to the /register endpoint, passing along the user data. If the registration was successful, we make use of the Auth module’s loginWith(), using the local strategy and passing the user data to log the user in. Then we redirect the user to the homepage. If there is an error during the registration, we set the error data as the error message gotten from the API response.

If there is an error, the error message is displayed by a Notification component, which we’ll create shortly.

Before we test the user registration out, let’s create the Notification component. Create a new Notification.vue file inside components and paste the code below in it:

// components/Notification.vue

<template>
<div class="notification is-danger">
{{ message }}
</div>
</template>

<script>
export default {
name: 'Notification',
props: ['message']
}
</script>

The Notification component accepts a message props, which is the error message.

Now, we can test out user registration:

Showing whether a user is logged in or not

Upon successful registration, we should be logged in but there is no way for us to know whether we are logged in or not for now. So let’s fix that by updating the Navbar component and adding some computed properties.

Before we do just that, let’s first activate the Vuex store by creating an index.js file inside the store directory. The Auth module stores user authentication status as well as user details inside Vuex state in an auth object. So we can check if a user is logged in or not with this.$store.state.auth.loggedIn, which will either return true or false. Similarly, we can get a user details with this.$store.state.auth.user, which will be null if no user is logged in.

Tips: We can also access the user authentication status as well as the user details directly with the Auth module using this.$auth.loggedIn and this.$auth.user respectively.

Since we might want to use the computed properties in multiple places in our app, let’s create store getters. Paste the code below into store/index.js:

// store/index.js

export const getters = {
isAuthenticated(state) {
return state.auth.loggedIn
},

loggedInUser(state) {
return state.auth.user
}
}

Here, we create two getters. The first one (isAuthenticated) will return the authentication status of a user and the second (loggedInUser) will return the details or the logged in user.

Next, let’s update the Navbar component to make use of the getters. Replace the content of components/Navbar.vue with the following:

// components/Navbar.vue

<template>
<nav class="navbar is-light">
<div class="container">
<div class="navbar-brand">
<nuxt-link class="navbar-item" to="/">Nuxt Auth</nuxt-link>
<button class="button navbar-burger">
<span></span>
<span></span>
<span></span>
</button>
</div>
<div class="navbar-menu">
<div class="navbar-end">
<div class="navbar-item has-dropdown is-hoverable" v-if="isAuthenticated">
<a class="navbar-link">
{{ loggedInUser.username }}
</a>
<div class="navbar-dropdown">
<nuxt-link class="navbar-item" to="/profile">My Profile</nuxt-link>
<hr class="navbar-divider">
<a class="navbar-item">Logout</a>
</div>
</div>
<template v-else>
<nuxt-link class="navbar-item" to="/register">Register</nuxt-link>
<nuxt-link class="navbar-item" to="/login">Log In</nuxt-link>
</template>
</div>
</div>
</div>
</nav>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
computed: {
...mapGetters(['isAuthenticated', 'loggedInUser'])
}
}
</script>

We create the computed properties by using the spread operator () to extract the getters from mapGetters. Then using isAuthenticated, we display the user menu or links to login or register depending on whether the user is logged in or not. Also, we use loggedInUser to display the authenticated user username.

Now, if we give our app a refresh, we should see something similar to below:

User log in

Now let’s allow returning users ability to login. Create a new login.vue file inside the pagesdirectory and paste the code below in it:

// pages/login.vue

<template>
<section class="section">
<div class="container">
<div class="columns">
<div class="column is-4 is-offset-4">
<h2 class="title has-text-centered">Welcome back!</h2>

      &lt;Notification :message="error" v-if="error"/&gt;

      &lt;form method="post" @submit.prevent="login"&gt;
        &lt;div class="field"&gt;
          &lt;label class="label"&gt;Email&lt;/label&gt;
          &lt;div class="control"&gt;
            &lt;input
              type="email"
              class="input"
              name="email"
              v-model="email"
            &gt;
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="field"&gt;
          &lt;label class="label"&gt;Password&lt;/label&gt;
          &lt;div class="control"&gt;
            &lt;input
              type="password"
              class="input"
              name="password"
              v-model="password"
            &gt;
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div class="control"&gt;
          &lt;button type="submit" class="button is-dark is-fullwidth"&gt;Log In&lt;/button&gt;
        &lt;/div&gt;
      &lt;/form&gt;
      &lt;div class="has-text-centered" style="margin-top: 20px"&gt;
        &lt;p&gt;
          Don't have an account? &lt;nuxt-link to="/register"&gt;Register&lt;/nuxt-link&gt;
        &lt;/p&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

</section>
</template>

<script>
import Notification from '~/components/Notification'

export default {
components: {
Notification,
},

data() {
return {
email: '',
password: '',
error: null
}
},

methods: {
async login() {
try {
await this.$auth.loginWith('local', {
data: {
email: this.email,
password: this.password
}
})

    this.$router.push('/')
  } catch (e) {
    this.error = e.response.data.message
  }
}

}
}
</script>

This is quite similar to the register page. The form contains two fields: email and password. When the form is submitted, a login method will be called. Using the Auth module loginWith() and passing along the user data, we log the user in. If the authentication was successful, we redirect the user to the homepage. Otherwise set error to the error message gotten from the API response. Again, we are using the Notification component from earlier on to display the error message.

Displaying the user profile

Let’s allow logged in user to be able to view their profile. Create a new profile.vue file inside the pages directory and paste the code below in it:

// pages/profile.vue

<template>
<section class="section">
<div class="container">
<h2 class="title">My Profile</h2>
<div class="content">
<p>
<strong>Username:</strong>
{{ loggedInUser.username }}
</p>
<p>
<strong>Email:</strong>
{{ loggedInUser.email }}
</p>
</div>
</div>
</section>
</template>

<script>
import { mapGetters } from 'vuex'

export default {
computed: {
...mapGetters(['loggedInUser'])
}
}
</script>

This is pretty straightforward, as we are using the loggedInUser getter from earlier on to display display the user details.

Clicking on the My Profile link should result in something similar to below:

Logging user out

Update the logout link inside the Navbar component as below:

// components/Navbar.vue

<a class="navbar-item" @click="logout">Logout</a>

When the logout link is click, it will trigger a logout method.

Next, let’s add the logout method inside the script section of the Navbar component:

// components/Navbar.vue

methods: {
async logout() {
await this.$auth.logout();
},
},

We call the logout() of the Auth module. This will simply delete the user’s token from localstorage and redirect the user to the homepage.

Restricting the profile page to only logged in users

As it stands now, anybody can visit the profile page and if the user is not logged in, it will result in error as below:

To fix this, we need to restrict the profile page to only logged in users. Luckily for us, we can easily achieve that with the Auth module. The Auth module comes with an auth middleware, which we can use in this scenario.

So let’s add the auth middleware to the profile page, update the script section as below:

// pages/profile.vue

<script>
...

export default {
middleware: 'auth',
...
}
</script>

Now when a user that is not logged in tries to visit the profile page, the user will be redirected to the login page.

Creating a guest middleware

Again as it stand, even as a logged in user, we can still access the login and register pages. One way to fix that is to restrict login and register pages to only users that are not logged in. We can do that by creating a guest middleware. Inside the middleware directory, create a new guest.js file and paste the code below in it:

// middleware/guest.js

export default function ({ store, redirect }) {
if (store.state.auth.loggedIn) {
return redirect('/')
}
}

A middleware accepts the context as it first argument. So we extract store and redirect from the context. We check if the user is logged in then redirect the user to the homepage. Otherwise, we allow the normal execution of the request.

Next, let’s make use of this middleware. Update the script section of both login and register as below:

<script>
...

export default {
middleware: 'guest',
...
}
</script>

Now everything should be working as expected.

Conclusion

That’s it! In this tutorial, we looked at how to implement authentication in a Nuxt.js application using the Auth module. We also saw how to keep the authentication flow sleek by making use of middleware.

Originally published on https://scotch.io


Nuxt.js: a Minimalist Framework for Creating Universal Vue.js Apps

Nuxt.js: a Minimalist Framework for Creating Universal Vue.js Apps

In this article, you'll learn how we can take advantage of Nuxt.js to build server-rendered JavaScript applications with Vue.js. Learn how to use its generate command to generate static files for our pages, and deploy them quickly via a service like Firebase Hosting.

In this article, you'll learn how we can take advantage of Nuxt.js to build server-rendered JavaScript applications with Vue.js. Learn how to use its generate command to generate static files for our pages, and deploy them quickly via a service like Firebase Hosting.

Universal (or Isomorphic) JavaScript is a term that has become very common in the JavaScript community. It’s used to describe JavaScript code that can execute both on the client and the server.

Many modern JavaScript frameworks, like Vue.js, are aimed at building single-page applications (SPAs). This is done to improve the user experience and make the app seem faster, since users can see updates to pages instantaneously. While this has a lot of advantages, it also has a couple of disadvantages, such as long “time to content” when initially loading the app as the browser retrieves the JavaScript bundle, and some search engine web crawlers or social network robots won’t see the entire loaded app when they crawl your web pages.

Server-side rendering of JavaScript is about preloading JavaScript applications on a web server and sending rendered HTML as the response to a browser request for a page.

Building server-side rendered JavaScript apps can be a bit tedious, as a lot of configuration needs to be done before you even start coding. This is the problem Nuxt.js aims to solve for Vue.js applications.

What Nuxt.js Is

Simply put, Nuxt.js is a framework that helps you build server-rendered Vue.js applications easily. It abstracts most of the complex configuration involved in managing things like asynchronous data, middleware, and routing. It’s similar to Angular Universal for Angular, and Next.js for React.

According to the Nuxt.js docs, “its main scope is UI rendering while abstracting away the client/server distribution.”

Static Generation

Another great feature of Nuxt.js is its ability to generate static websites with the generate command. It’s pretty cool, and provides features similar to popular static generation tools like Jekyll.

Under the Hood of Nuxt.js

In addition to Vue.js 2.0, Nuxt.js includes the following: Vue-Router, Vuex (only included when using the store option), Vue Server Renderer and vue-meta. This is great, as it takes away the burden of manually including and configuring different libraries needed for developing a server-rendered Vue.js application. Nuxt.js does all this out of the box, while still maintaining a total size of 57kB min+gzip (60KB with vuex).

Nuxt.js also uses webpack with vue-loader and babel-loader to bundle, code-split and minify code.

How it works

This is what happens when a user visits a Nuxt.js app or navigates to one of its pages via <nuxt-link>:

  1. When the user initially visits the app, if the [nuxtServerInit](https://nuxtjs.org/guide/vuex-store/#the-nuxtserverinit-action "nuxtServerInit") action is defined in the store, Nuxt.js will call it and update the store.
  2. Next, it executes any existing middleware for the page being visited. Nuxt checks the nuxt.config.js file first for global middleware, then checks the matching layout file (for the requested page), and finally checks the page and its children for middleware. Middleware are prioritized in that order.
  3. If the route being visited is a dynamic route, and a validate() method exists for it, the route is validated.
  4. Then, Nuxt.js calls the asyncData() and fetch() methods to load data before rendering the page. The [asyncData()](https://nuxtjs.org/guide/async-data/ "asyncData()") method is used for fetching data and rendering it on the server-side, while the [fetch()](https://nuxtjs.org/api/pages-fetch/ "fetch()") method is used to fill the store before rendering the page.
  5. At the final step, the page (containing all the proper data) is rendered.

These actions are portrayed properly in this schema, gotten from the Nuxt docs:

Creating A Serverless Static Site With Nuxt.js

Let’s get our hands dirty with some code and create a simple static generated blog with Nuxt.js. We’ll assume our posts are fetched from an API and will mock the response with a static JSON file.

To follow along properly, a working knowledge of Vue.js is needed. You can check out Jack Franklin’s great getting started guide for Vue.js 2.0 if you’re new to the framework. I’ll also be using ES6 Syntax, and you can get a refresher on that here: sitepoint.com/tag/es6/.

Our final app will look like this:

The entire code for this article can be seen here on GitHub, and you can check out the demo here.

Application Setup and Configuration

The easiest way to get started with Nuxt.js is to use the template created by the Nuxt team. We can install it to our project (ssr-blog) quickly using the vue-cli:

vue init nuxt/starter ssr-blog


Once you’ve run this command, a prompt will open and ask you a couple of questions. You can press Return to accept the default answers, or enter values of your own.

Note: If you don’t have vue-cli installed, you have to run *npm install -g @vue/cli* first, to install it.

Next, we install the project’s dependencies:

cd ssr-blog
npm install


Now we can launch the app:

npm run dev


If all goes well, you should be able to visit http://localhost:3000 to see the Nuxt.js template starter page. You can even view the page’s source, to see that all content generated on the page was rendered on the server and sent as HTML to the browser.

Next, we can make some simple configurations in the nuxt.config.js file. We’ll add a few options:

// ./nuxt.config.js

module.exports = {
  /*
   * Headers of the page
   */
  head: {
    titleTemplate: '%s | Awesome JS SSR Blog',
    // ...
    link: [
      // ...
      {
        rel: 'stylesheet',
        href: 'https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css'
      }
    ]
  },
  // ...
}


In the above config file, we simply specify the title template to be used for the application via the titleTemplate option. Setting the title option in the individual pages or layouts will inject the title value into the %s placeholder in titleTemplate before being rendered.

We also pulled in my current CSS framework of choice, Bulma, to take advantage of some preset styling. This was done via the link option.

Note: Nuxt.js uses vue-meta to update the headers and HTML attributes of our apps. So you can take a look at it for a better understanding of how the headers are being set.

Now we can take the next couple of steps by adding our blog’s pages and functionalities.

Working with Page Layouts

First, we’ll define a custom base layout for all our pages. We can extend the main Nuxt.js layout by updating the layouts/default.vue file:

<!-- ./layouts/default.vue -->

<template>
  <div>
    <!-- navigation -->
    <nav class="navbar has-shadow" role="navigation" aria-label="main navigation">
      <div class="container">
        <div class="navbar-start">
          <nuxt-link to="/" class="navbar-item">
            Awesome JS SSR Blog!
          </nuxt-link>
          <nuxt-link active-class="is-active" to="/" class="navbar-item is-tab" exact>Home</nuxt-link>
          <nuxt-link active-class="is-active" to="/about" class="navbar-item is-tab" exact>About</nuxt-link>
        </div>
      </div>
    </nav>
    <!-- /navigation -->

    <!-- displays the page component -->
    <nuxt/>

  </div>
</template>

<style>
  .main-content {
    margin: 30px 0;
  }
</style>


In our custom base layout, we add the site’s navigation header. We use the <nuxt-link> component to generate links to the routes we want to have on our blog. You can check out the docs on [<nuxt-link>](https://nuxtjs.org/api/components-nuxt-link "<nuxt-link>") to see how it works.

The <nuxt> component is really important when creating a layout, as it displays the page component.

It’s also possible to do a couple of more things — like define custom document templates and error layouts — but we don’t need those for our simple blog. I urge you to check out the Nuxt.js documentation on views to see all the possibilities.

Simple Pages and Routes

Pages in Nuxt.js are created as single file components in the pages directory. Nuxt.js automatically transforms every .vue file in this directory into an application route.

Building the Blog Homepage

We can add our blog homepage by updating the index.vue file generated by the Nuxt.js template in the pages directory:

<!-- ./pages/index.vue -->
<template>
  <div>
    <section class="hero is-medium is-primary is-bold">
      <div class="hero-body">
        <div class="container">
          <h1 class="title">
            Welcome to the JavaScript SSR Blog.
          </h1>
          <h2 class="subtitle">
            Hope you find something you like.
          </h2>
        </div>
      </div>
    </section>
  </div>
</template>

<script>
  export default {
    head: {
      title: 'Home'
    }
  }
</script>

<!-- Remove the CSS styles -->


As stated earlier, specifying the title option here automatically injects its value into the titleTemplate value before rendering the page.

We can now reload our app to see the changes to the homepage.

Building the About page

Another great thing about Nuxt.js is that it will listen to file changes inside the pages directory, so there’s no need to restart the application when adding new pages.

We can test this, by adding a simple about.vue page:

<!-- ./pages/about.vue -->
<template>
  <div class="main-content">
    <div class="container">
      <h2 class="title is-2">About this website.</h2>
      <p>Curabitur accumsan turpis pharetra <strong>augue tincidunt</strong> blandit. Quisque condimentum maximus mi, sit amet commodo arcu rutrum id. Proin pretium urna vel cursus venenatis. Suspendisse potenti. Etiam mattis sem rhoncus lacus dapibus facilisis. Donec at dignissim dui. Ut et neque nisl.</p>
      <br>
      <h4 class="title is-4">What we hope to achieve:</h4>
      <ul>
        <li>In fermentum leo eu lectus mollis, quis dictum mi aliquet.</li>
        <li>Morbi eu nulla lobortis, lobortis est in, fringilla felis.</li>
        <li>Aliquam nec felis in sapien venenatis viverra fermentum nec lectus.</li>
        <li>Ut non enim metus.</li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  head: {
    title: 'About'
  }
}
</script>


Now, we can visit http://localhost:3000/about to see the about page, without having to restart the app, which is awesome.

Showing Blog Posts on the Homepage

Our current homepage is pretty bare as it is, so we can make it better by showing the recent blog posts from the blog. We’ll do this by creating a <posts> component and displaying it in the index.vue page.

But first, we have to get our saved JSON blog posts and place them in a file in the app root folder. The file can be downloaded from here, or you can just copy the JSON below and save in the root folder as posts.json:

[
    {
        "id": 4,
        "title": "Building universal JS apps with Nuxt.js",
        "summary": "Get introduced to Nuxt.js, and build great SSR Apps with Vue.js.",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>",
        "author": "Jane Doe",
        "published": "08:00 - 07/06/2017"
    },
    {
        "id": 3,
        "title": "Great SSR Use cases",
        "summary": "See simple and rich server-rendered JavaScript apps.",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>",
        "author": "Jane Doe",
        "published": "17:00 - 06/06/2017"
    },
    {
        "id": 2,
        "title": "SSR in Vue.js",
        "summary": "Learn about SSR in Vue.js, and where Nuxt.js can make it all faster.",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>",
        "author": "Jane Doe",
        "published": "13:00 - 06/06/2017"
    },
    {
        "id": 1,
        "title": "Introduction to SSR",
        "summary": "Learn about SSR in JavaScript and how it can be super cool.",
        "content": "<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>",
        "author": "John Doe",
        "published": "11:00 - 06/06/2017"
    }
]


Note: Ideally the posts should be retrieved from an API or resource. For example, Contentful is a service that can be used for this.

Components live in the components directory. We’ll create the <posts> single file component in there:

<!-- ./components/Posts.vue -->
<template>
  <section class="main-content">
    <div class="container">
      <h1 class="title has-text-centered">
        Recent Posts.
      </h1>
      <div class="columns is-multiline">
        <div class="column is-half" v-for="post in posts" :key="post.id">
          <div class="card">
           <header class="card-header">
            <p class="card-header-title">
              {{ post.title }}
            </p>
          </header>
          <div class="card-content">
            <div class="content">
              {{ post.summary }}
              <br>
              <small>
                by <strong>{{ post.author}}</strong>
                \\ {{ post.published }}
              </small>
            </div>
          </div>
          <footer class="card-footer">
            <nuxt-link :to="`/post/${post.id}`"
              class="card-footer-item">
              Read More
            </nuxt-link>
          </footer>
        </div>
      </div>
    </div>
  </div>
</section>
</template>

<script>
  import posts from '~/posts.json'

  export default {
    name: 'posts',
    data () {
      return { posts }
    }
  }
</script>


We import the posts data from the saved JSON file and assign it to the posts value in our component. We then loop through all the posts in the component template with the v-for directive and display the post attributes we want.

Note: The *~* symbol is an alias for the */* directory. You can check out the docs here to see the different aliases Nuxt.js provides, and what directories they’re linked to.

Next, we add the <posts> component to the homepage:

<!-- ./pages/index.vue -->
<template>
<div>
    <!-- ... -->
    <posts />
</div>
</template>

<script>
import Posts from '~/components/Posts.vue'

export default {
  components: {
    Posts
  },
  // ...
}
</script>


Adding Dynamic Routes

Now we’ll add dynamic routes for the posts, so we can access a post for example with this URL: /post/1.

To achieve this, we add the post directory to the pages directory and structure it like this:

pages
└── post
    └── _id
        └── index.vue


This generates the corresponding dynamic routes for the application like this:

router: {
  routes: [
    // ...
    {
      name: 'post-id',
      path: '/post/:id',
      component: 'pages/post/_id/index.vue'
    }
  ]
}


Updating the single post file:

<!-- ./pages/post/_id/index.vue -->
<template>
  <div class="main-content">
    <div class="container">
      <h2 class="title is-2">{{ post.title }}</h2>
      <div v-html="post.content"></div>
      <br>
      <h4 class="title is-5 is-marginless">by <strong>{{ post.author }}</strong> at <strong>{{ post.published }}</strong></h4>
    </div>
  </div>
</template>

<script>
  // import posts saved JSON data
  import posts from '~/posts.json'

  export default {
    validate ({ params }) {
      return /^\d+$/.test(params.id)
    },
    asyncData ({ params }, callback) {
      let post = posts.find(post => post.id === parseInt(params.id))
      if (post) {
        callback(null, { post })
      } else {
        callback({ statusCode: 404, message: 'Post not found' })
      }
    },
    head () {
      return {
        title: this.post.title,
        meta: [
          {
            hid: 'description',
            name: 'description',
            content: this.post.summary
          }
        ]
      }
    }
  }
</script>


Nuxt.js adds some custom methods to our page components to help make the development process easier. See how we use some of them on the single post page:

  • Validate the route parameter with the validate method. Our validate method checks if the route parameter passed is a number. If it returns false, Nuxt.js will automatically load the 404 error page. You can read more on it here.
  • The asyncData method is used to fetch data and render it on the server side before sending a response to the browser. It can return data via different methods. In our case, we use a callback function to return the post that has the same id attribute as the route id parameter. You can see the various ways of using this function here.
  • As we’ve seen before, we use the head method to set the page’s headers. In this case, we’re changing the page title to the title of the post, and adding the post summary as a meta description for the page.

Great, now we can visit our blog again to see all routes and pages working properly, and also view the page source to see the HTML being generated. We have a functional server-rendered JavaScript application.

Generating Static Files

Next, we can generate the static HTML files for our pages.

We’ll need to make a minor tweak though, as by default Nuxt.js ignores dynamic routes. To generate the static files for dynamic routes, we need to specify them explicitly in the ./nuxt.config.js file.

We’ll use a callback function to return the list of our dynamic routes:

// ./nuxt.config.js

module.exports = {
  // ...
  generate: {
    routes(callback) {
      const posts = require('./posts.json')
      let routes = posts.map(post => `/post/${post.id}`)
      callback(null, routes)
    }
  }
}


You can check here for the full documentation on using the generate property.

To generate all the routes, we can now run this command:

npm run generate


Nuxt saves all generated static files to a dist folder.

Deployment on Firebase Hosting

As a final step, we can take advantage of hosting by Firebase to make our static website live in a couple of minutes. This step assumes that you have a Google account.

First, install the Firebase CLI, if you don’t already have it:

npm install -g firebase-tools


To connect your local machine to your Firebase account and obtain access to your Firebase projects, run the following command:

firebase login


This should open a browser window and prompt you to sign in. Once you’re signed in, visit https://console.firebase.google.com and click Add project. Make the relevant choices in the wizard that opens.

Once the project is created, go to the project’s hosting page at [https://console.firebase.google.com/project/<project](https://console.firebase.google.com/project/<project "https://console.firebase.google.com/project/<project") name>/hosting and complete the Get started wizard.

Then, on your PC, from the root of your project directory, run the following command:

firebase init


In the wizard that appears, select “Hosting”. Then select your newly created project from the list of options. Next choose the dist directory as the public directory. Select to configure the page as a single-page app and finally select “No” when asked if you want to overwrite dist/index.html.

Firebase will write a couple of configuration files to your project, then put the website live at [https://<project](https://<project "https://<project") name>.firebaseapp.com. The demo app for this article can be seen at nuxt-ssr-blog.firebaseapp.com.

If you run into problems, you can find full instructions on Firebase’s quickstart page.

Conclusion

In this article, we’ve learned how we can take advantage of Nuxt.js to build server-rendered JavaScript applications with Vue.js. We also learned how to use its generate command to generate static files for our pages, and deploy them quickly via a service like Firebase Hosting.

The Nuxt.js framework is really great. It’s even recommended in the official Vue.js SSR GitBook. I really look forward to using it in more SSR projects and exploring all of its capabilities.

Nuxt.js cheat sheet

Nuxt.js cheat sheet

Nuxt.js is a higher-level framework that builds on top of Vue. It simplifies the development of universal or single page Vue apps. Nuxt.js is not a server side framework. It runs on the servers. It renders the first page and after the first page is renderd, the Vue.js app takes over.

Nuxt.js is a higher-level framework that builds on top of Vue. It simplifies the development of universal or single page Vue apps. Nuxt.js is not a server side framework. It runs on the servers. It renders the first page and after the first page is renderd, the Vue.js app takes over.

Nuxt.js is here to make your life easy, it's also here to make the Vue.js development process even nicer than it already is. But with all its good aspects, it has quirks that will have you click on every single link on Google.

This article is here to avoid these situations, it'll cover some normal use-cases and some edge-cases with quick and easy code snippets. It won't go into extreme detail on these matters, but will give you the documentation necessary to do so in case you want to.

Note: You'll need a good grasp of Vue.js concepts to take full advantage of this article !
Before we get into anything concrete, let me explain what Nuxt.js is.

What's Nuxt.js?

Nuxt.js is a framework based on Vue.js that allows you to build fully fledged server-rendered applications.

It comes out of the box with loads of useful packages:

  • 💻 Vue
  • ↩️ Vue Router (for easy routing)
  • 💾 Vuex (for easy state management)
  • 🏎 Vue Server Renderer (for server-side rendering out of the box)
  • 🕵️‍♂️ Vue meta (for SEO)

Here's a list of what we'll cover (feel free to come back here if you're searching for something specific):

General

  • Creating a Nuxt.js project
  • Testing with Nuxt.js

Routing

  • Creating a new route
  • Creating dynamic routes
  • Navigating to a route in a component template
  • Navigating to a route programatically

State management

  • Creating a new store module
  • Updating a store before rendering a component

SEO

Miscellaneous

  • Displaying a fixed component throughout your app
  • Changing a project's router base
  • Handling internationalization (i18n)
  • Importing a font to your project

If you have any other requests or want to add anything new, please feel free to hit me up on Twitter @christo_kade !

Creating a Nuxt.js project
yarn create nuxt-app <project-name>

Which will prompt you to answer some questions, including:

  • Choose between integrated server-side frameworks (None by default, Express, Koa etc.)
  • Choose features to install (PWA Support, Linter / Formatter, Prettier, Axios)
  • Choose your favorite UI framework (None by default, Bootstrap, Vuetify, Bulma etc.)
  • Choose your favorite testing framework (None, Jest, AVA)
  • The Nuxt mode you want (Universal or SPA, more information)

Once done and your dependencies are installed:

$ cd <project-name>
$ yarn dev

Documentation

Testing with Nuxt.js

The majority of your testing syntax will depend on the testing framework chosen during the project's creation.

Out of the box, Nuxt uses the @vue/test-utils package to render your components thanks to multiple methods such as mount(), shallowMount()and render(). You'll then be able to test that specific values have been displayed, that specific methods were called etc.

Nuxt will also make sure to set everything up for you, all you'll have to do is create your *.spec.js or *.test.js files and run the yarn test command.

Here's a classic (and brief) example of unit testing for a Vue component in a Nuxt project:

import { shallowMount } from "@vue/test-utils"
import cmp from "~/components/navbar/navbar"

// Mocking an icon displayed in my navbar
jest.mock("~/static/icons/svg/icon-menu.svg", () => "")

describe("Navbar component", () => {

  // We shallow mount the component while mocking some internal elements
  // Most of the time, you'll have to mock context objects such as $store or $route in order to render your component whithout any errors
  const wrapper = shallowMount(cmp, {
    // Stubbing nuxt-links in the navbar
    stubs: ["nuxt-link"],
    mocks: {
      "nuxt-Link": true,
      // Mocking the $store context object
      $store: {
        state: {
          locale: "en",
        },
      },
      // Mocking the $route context object
      $route: {
        path: "mockedPath",
      },
    },    
  })

  it("Snapshot testing", () => {
    expect(wrapper.html()).toMatchSnapshot()
  })

  describe("Components validation", () => {
    it("should return a valid component", () => {
      expect(wrapper.is(cmp)).toBe(true)
    })
  })

  describe("Content validation", () => {
    it("should render the link's name", () => {
      expect(wrapper.html()).toContain("About")
    })

    // ...
  })
})

Documentation

Creating a new route

In the /pages folder, create a file, its name will be the name of the route.

So for example:

// /pages/about.vue

<template>
  <main>
    <h1>About page</h1>
  <main/>
</template>

<script>
export default {}
</script>

<style></style>

Navigating to localhost:3000/about will display this component's content

Documentation

Creating dynamic routes

In the /pages folder, create a directory and a file prefixed by an underscore.

For example, the following file tree:

pages/
--| users/
-----| _id.vue
--| index.vue

Will automatically generate the following router inside the .nuxt folder whenever you build your project:

router: {
  routes: [
    {
      name: 'index',
      path: '/',
      component: 'pages/index.vue'
    },
    {
      name: 'users-id',
      path: '/users/:id?',
      component: 'pages/users/_id.vue'
    },
  ]
}

You can now navigate to /users/:id, with id being whatever value you need it to be.

To retrieve this value in your _id.vue component, just do the following:

// $route is a Nuxt context object, more info: https://nuxtjs.org/api/context
const { id } = this.$route.params

Documentation, including nested routes and dynamic nested routes.

Navigating to a route in a component template

Inside of any of your components:

// /components/example.vue

// Clicking on this nuxt-link will navigate to the /pages/about.vue component
// nuxt-link renders an <a> tag in your HTML
<template>
  <section>    
    <nuxt-link to="/about">
      About
    </nuxt-link>
  </section>
</template>

// ...

Documentation

Navigating to a route programatically
// Will add a history entry to the stack
this.$router.push({
  path: '/about'
})

// Will not
this.$router.replace({
  path: '/about'
})

// Goes back one record
this.$router.go(-1)

Creating a new store module

In the /store folder, each file is a Vuex module.

// /store/todos.js
export const state = () => ({
  list: []
})

export const mutations = {
  add(state, text) {
    state.list.push({
      text: text,
      done: false
    })
  },
  remove(state, { todo }) {
    state.list.splice(state.list.indexOf(todo), 1)
  },
  toggle(state, todo) {
    todo.done = !todo.done
  }
}

Each module's mutations, actions & states are now available using the context object $store:

// /components/todo.vue
<template>
  <ul>
    <li v-for="todo in todos">
      <input type="checkbox" :checked="todo.done" @change="toggle(todo)">
      <span>{{ todo.text }}</span>
    </li>
    <li><input placeholder="What needs to be done?" @keyup.enter="addTodo"></li>
  </ul>
</template>

<script>
import { mapMutations } from 'vuex'

export default {
  computed: {
    todos () {
      return this.$store.state.todos.list // highlight-line
    }
  },
  methods: {
    addTodo (e) {
      this.$store.commit('todos/add', e.target.value) // highlight-line
      e.target.value = ''
    },
    ...mapMutations({ // highlight-line
      toggle: 'todos/toggle' // highlight-line
    }) // highlight-line
  }
}
</script>

Documentation

Updating a store before rendering a component

Sometimes you need to fill up a given state variable before rendering a component, here's how:

// In any component

export default {
  // Called before rendering the component
  fetch ({ store, params }) {
    return axios.get('https://dog.ceo/api/breeds/image/random')
    .then((res) => {
      store.commit('setDog', res.data.message)
    })
  }
}

Warning: You don't have access of the component instance through this inside fetch because it is called before initiating the component (read more).
Documentation

Changing a page's head properties dynamically

For SEO purposes, defining the page's title, description keywords etc. can be useful. Here's how you can do it programmatically:

// In any component
export default {
  head: {
    title: 'Page title',
    meta: [
      { 
        hid: 'description', name: 'description', 
        content: 'Page description' 
      }
    ],
    // ...
  }
}

Info: To avoid duplicated meta tags when used in child component, set up an unique identifier with the hid key for your meta elements (read more).
Documentation

SSR for dynamic routes

When running nuxt generate, the HTML file for your dynamic routes won't be generated by default.

For example, if you have an about.vue page and a _id.vue one, when running nuxt generate, the resulting dist folder will contain /about/index.html but won't generate anything for your dynamic _id.vue.

This can lead to your dynamic routes to be missed by crawlers, and therefore not referenced by search engines !

Here's how you can generate them automacially:

// nuxt.config.js

module.exports = {
  // ...

  // dynamicRoutes could be a JSON file containing your dynamic routes
  // or could be retrieved automatically based on the content of your /pages folder
  generate: {
    routes: () => {
      return dynamicRoutes.map(route => `/articles/${route}`)
    },
  },

  // ...
}

nuxt generate will now generate the HTML file for each dynamic route returned by the generate property.

Documentation

Displaying a fixed component throughout your app

Sometimes you need to add a navbar or a footer that will be displayed no matter the current route.

There's a /layout folder that contains default.vue by default. This layout holds the <nuxt/> component that takes care of rendering the content of each one of your pages (see Creating a new route).

Simply modify that component to fit your needs, for example:

<template>
  <div>
    <navbar/>
    <nuxt/>
    <footer/>
  </div>
</template>

<script>
import navbar from "~/components/navbar/navbar"
import footer from "~/components/footer/footer"

export default {
  components: {
    cmpNavbar,    
    cmpFooter,
  },  
}
</script>

Documentation

Changing a project's router base

In some cases, when for example you're deploying your project on Github Pages under username/my-project, you'll need to change the project's router base so that your assets are displayed correctly.

// nuxt.config.js

// Will change the router base to /my-project/ when DEPLOY_ENV equals GH_PAGES
const routerBase = process.env.DEPLOY_ENV === "GH_PAGES"
  ? {
    router: {
      base: "/my-project/",
    },
  }
  : {
    router: {
      base: "/",
    },
  }

module.exports = {  
  // ...
  routerBase,
  // ...
}

And don't forget to change your package.json so that nuxt.config.jsknows when you're building or generating for Github Pages.

// package.json

"scripts": {
  "build:gh-pages": "DEPLOY_ENV=GH_PAGES nuxt build",
  "generate:gh-pages": "DEPLOY_ENV=GH_PAGES nuxt generate"
},

Handling internationalization (i18n)

Start by running yarn add vue-i18n

Create the following file:

// /plugins/i18n.js

import Vue from "vue"
import VueI18n from "vue-i18n"

Vue.use(VueI18n)

export default ({ app, store }) => {

  // Set i18n instance on app
  // This way we can use it globally in our components
  app.i18n = new VueI18n({
    locale: store.state.locale,
    fallbackLocale: "fr",
    messages: {
      // Add the supported languages here AND their associated content files
      en: require("~/static/json/data-en.json"),
      fr: require("~/static/json/data-fr.json"),      
    },
  })
}

And add the following line in your nuxt.config.js to inform it we're using that plugin:

module.exports = {
  // ...
  plugins: ["~/plugins/i18n.js"],
  // ...
}

In this example, the current locale is based on my store's content, which looks like so:

export const state = () => ({
  locales: ["en", "fr"],
  locale: "fr",
})

export const mutations = {
  setLanguage(state, locale) {
    if (state.locales.indexOf(locale) !== -1) {
      state.locale = locale
    }
  },
}

So whenever we call setLanguage, the locale is automatically updated and the correct JSON file is loaded ! ✨

Your file contents are now available throughout your application like so:

// Here we access the 'users' array in our JSON file
this.$t("users")

Documentation

Importing a font to your project
// nuxt.config.js

module.exports = {
  /*
   ** Headers of the page
   */
  head: {    
    // ...
    link: [
      {
        rel: "stylesheet",
        href: "https://fonts.googleapis.com/css?family=Lato",
      },
    ],
  },

  // ...
}

Wrapping up

Alright, I believe that's enough for one article. I've covered lots of use-cases which hopefully will be useful to some of you.

How to set up Nuxt.js with Nginx ?

I would like to ask for some tutorial or advice on how to set up Nuxt.js + Express.js (whatever) with Nginx. I have some problems with loading and caching files by Nginx. My public path is wrong.

I would like to ask for some tutorial or advice on how to set up Nuxt.js + Express.js (whatever) with Nginx. I have some problems with loading and caching files by Nginx. My public path is wrong.