Adding a Server-Side Framework

In this chapter, you will learn how to configure Nuxt with a server-side framework, and how to use the asyncData method to fetch the data from the server-side framework, such as Koa or Express. Setting up a server-side framework with Nuxt is fairly easy. We only need to pick a framework as the first-class citizen and use Nuxt as the middleware. We can use npx create-nuxt-app <project-name> to set that up for us, but we will walk you through how to do that manually so that you have a better understanding of how these two apps work together. Additionally, in this chapter, we will use Backpack as the build system for our app.

The topics we will cover in this chapter are as follows:

  • Introducing Backpack
  • Introducing Koa
  • Integrating Koa with Nuxt
  • Understanding async data
  • Accessing context in asyncData
  • Fetching async data with Axios

Introducing Backpack

Backpack is a build system for building modern Node.js apps with zero or minimal configuration. It supports the latest JavaScript and handles the file watching, live reloading, transpiling, and bundling that we have been doing with webpack in the previous chapters. We can think of it as a wrapper of webpack, a simplified version of the webpack configuration that we have been using in this book so far. You can find out more info about Backpack at https://github.com/jaredpalmer/backpack. Now, let's find out how we can use it to speed up our app development in the coming sections.

Installing and configuring Backpack

Creating a modern Node.js app with Backpack can be as easy as implementing the following steps:

  1. Install Backpack via npm:
$ npm i backpack-core
  1. Create a /src/ directory and a package.json file in the project root with backpack in the dev script as follows:
{
"scripts": {
"dev": "backpack"
}
}
Note that you must provide the /src/ as the default entry directory of your app.
  1. Create a Backpack config file in your project root with a function to configure webpack as follows:
// backpack.config.js
module.exports = {
webpack: (config, options, webpack) => {
// ....
return config
}
}

This step is optional, but it is useful if you want to change the default entry directory (which is the /src/ directory that you created in step 2) of your app to a different one, for example, a /server/ directory, this can be done as follows:

webpack: (config, options, webpack) => {
config.entry.main = './server/index.js'
return config
}
  1. Start your app in development mode with the following command:
$ npm run dev

Then you can develop the source code of your app in the /server/ directory and browse to the app on the browser at whatever port you have set it to. Let's create a simple Express app with Backpack in the next section.

Creating a simple app using Backpack

Creating an Express app with Backpack can be as easy as implementing the following steps:

  1. Install Express via npm:
$ npm i express
  1. Add build and start scripts after the dev script in the package.json file:
// package.json
"scripts": {
"dev": "backpack",
"build": "backpack build",
"start": "cross-env NODE_ENV=production node build/main.js"
}
  1. Create the Backpack config file and use /server/ as the entry folder of your app, just as we have walked you through in the previous section:
// backpack.config.js
module.exports = {
webpack: (config, options, webpack) => {
config.entry.main = './server/index.js'
return config
}
}
  1. Create a simple route with a 'Hello World' message:
// server/index.js
import express from 'express'
const app = express()
const port = 3000

app.get('/', (req, res) =>
res.send('Hello World')
)

app.listen(port, () =>
console.log(Example app listening on port ${port}!)
)
  1. Run your app in development mode:
$ npm run dev

You can now browse to your app on a browser at 127.0.0.1:3000. You should see Hello World on the screen. You can find this example in /chapter-8/backpack/ in our GitHub repository. Next, let's use Koa as the server-side framework that allows us to write ES2015 code and async functions in fewer lines than Express in the next section.

Introducing Koa

Koa is a Node.js web framework designed by the same team that brought you Express. The main goal of this framework is to be a smaller and more expressive foundation for web apps and APIs. If you have ever worked on Express and have gotten tired of callback hell when the app gets larger, Koa allows you to ditch the callbacks and greatly increase error handling by leveraging async functions. Another cool thing in Koa is cascading – the middleware you add will be running "downstream," and then flowing back "upstream," which gives you more predictable controls. We will demonstrate this later in this chapter.

If you want to find out more info about Koa, please visit https://koajs.com/.

Installing and configuring Koa

Now, let's create a Koa app with the Backpack default configuration (without creating the Backpack config file) as follows:

  1. Install Koa via npm:
$ npm i koa
  1. Use /src/ as the Backpack default entry directory and create an entry file in this directory with minimal code in Koa style as follows:
// src/index.js
const Koa = require('koa')
const app = new Koa()

app.use(async ctx => {
ctx.body = 'Hello World'
})
app.listen(3000)
  1. Run the Koa app in development mode:
$ npm run dev

You should see Hello World on the screen when browsing the app on a browser at 127.0.0.1:3000. If you have been using Express to create your Node.js apps, you can see that Koa is an alternative that can be used to do the same thing with neater code. Next, let's learn what a Koa context is and how cascading works in Koa in the following sections.

What is ctx?

You may have wondered what that ctx is in the minimal code we created in the previous section and where the req and res objects are, because they are there in the Express app. They are not gone in Koa. They are just encapsulated in a single object in Koa, which is the Koa context, referred to as ctx. We can access the request and response objects as follows:

app.use(async ctx => {
ctx.request
ctx.response
})

So, you can see that we can easily use ctx.request to access the Node.js request object and ctx.response for the Node.js response object. These two important HTTP objects are not gone in Koa! They are just tucked away in the Koa context – ctx. Next, let's find out how cascading works in Koa in the next section.

Understanding how Koa cascading works

In a nutshell, the cascading in Koa works by invoking middleware downstream sequentially and then controlling them to flow back upstream sequentially. It is best to create a simple Koa app to demonstrate this great feature in Koa:

  1. Create an index.js file in the /src/ directory, just like we did in the previous section:
// src/index.js
const Koa = require('koa')
const app = new Koa()

app.use(async ctx => {
console.log('Hello World')
ctx.body = 'Hello World'
})
app.listen(3000)
  1. Create three pieces of middleware just before the Hello World middleware as follows, so that we can run them first:
app.use(async (ctx, next) => {
console.log('Time started at: ', Date.now())
await next()
})

app.use(async (ctx, next) => {
console.log('I am the first')
await next()
console.log('I am the last')
})

app.use(async (ctx, next) => {
console.log('I am the second')
await next()
console.log('I am the third')
})
  1. Run the app in development mode and you should get the following output on the terminal:
Time started at: 1554647742894
I am the first
I am the second
Hello World
I am the third
I am the last

In this demonstration, the request flows through Time started at: to I am the first, I am the second, and reaching Hello World. When there is no more middleware to be executed downward (downstream), each middleware will be unwound and resumed upward (upstream) in the following sequence: I am the third, I am the last.

You can find this example in /chapter-8/koa/cascading/ in our GitHub repository.

Next, we will walk you through some dependencies that you should install for developing a full stack Koa app so that it works just like Express apps.

Installing dependencies for Koa apps

Koa is minimalistic. It is barebones by nature. Hence, it does not have any middleware within its core. Express comes with a router, which, by default, Koa does not have. This can be a challenge when writing an app in Koa as you need to choose a third-party package or pick one of the packages listed on their GitHub main page at https://github.com/koajs. You may test out a few and find out they don't work as you want. There are a few Koa packages that can be used for routing; koa-router is mostly used in this book, alongside other essential dependencies for developing our API with Koa. Let's discover what they are and what they do by installing them and creating a skeleton app, as follows:

  1. Install the koa-router module and use it as follows:
$ npm i koa-router

Import koa-router in the entry file with a home route, /, as follows:

// src/index.js
const Router = require('koa-router')
const router = new Router()

router.get('/', (ctx, next) => {
ctx.body = 'Hello World'
})

app
.use(router.routes())
.use(router.allowedMethods())

You can find more information about this middleware at https://github.com/koajs/koa-router from Koa's GitHub repository. This module is forked from ZijianHe/koa-router (https://github.com/ZijianHe/koa-router). It is the most widely used router module in the Koa community. It provides Express-style routing using app.get, app.put, app.post, and so on. It also supports other important features, such as multiple route middleware and multiple and nestable routers.

  1. Install the koa-bodyparser module and use it as follows:
$ npm i koa-bodyparser

Import koa-bodyparser in the entry file, register it, and create a home route, /post, as follows:

// src/index.js
const bodyParser = require('koa-bodyparser')
app.use(bodyParser())

router.post('/post', (ctx, next) => {
ctx.body = ctx.request.body
})

You can find more information about this middleware at https://github.com/koajs/bodyparser from Koa's GitHub repository. You may wonder: what is a body parse anyway? When we are dealing with HTML forms, we use application/x-www-form-urlencoding or multipart/form-data to transact data between the client and server sides, for example:

// application/x-www-form-urlencoding
<form action="/update" method="post">
//...
</form>

// multipart/form-data
<form action="/update" method="post" encrypt="multipart/form-data">
//...
</form>

The default type for HTML forms is application/x-www-urlencoded, and if we want to read the data of HTTP POST, PATCH, and PUT, we use a body parser, which is a middleware that parses the incoming request, assembles the chunks containing the form data, and then creates a body object filled with the form data so that we can access them from the request object in the ctx object as follows:

ctx.body = ctx.request.body
  1. Install the koa-favicon module and use it as follows:
$ npm i koa-favicon

Import koa-favicon in the entry file and register it with a path to favicon as follows:

// src/index.js
const favicon = require('koa-favicon')
app.use(favicon('public/favicon.ico'))

You can find more information about this middleware at https://github.com/koajs/favicon from Koa's GitHub repository. It is a middleware that serves a favicon, so let's create a favicon.ico file and keep it in the /public folder in the project root. You should see the favicon on your browser tab when you refresh the home page.

  1. Install the koa-static module and use it as follows:
$ npm i koa-static

Import koa-static in the entry file and register it with the following paths as follows:

const serve = require('koa-static')
app.use(serve('.'))
app.use(serve('static/fixtures'))

You can find more information about this middleware at https://github.com/koajs/static from Koa's GitHub repository. Koa, by default, doesn't allow you to serve static files. So this middleware will allow you to serve static files from your API. For example, the paths we just set will let us access the following files from the /static folder in the project root:

  • GET /package.json at 127.0.0.1:3000/package.json.
  • GET /hello.txt at 127.0.0.1:3000/hello.txt.

We will use this skeleton in future chapters when creating APIs with Koa. Now, let's discover how we can integrate Koa with Nuxt in the next section.

You can find this skeleton app at /chapter-8/koa/skeleton/ in our GitHub repository. 

Integrating Koa with Nuxt

Integrating Koa and Nuxt can be done on a single port for single-domain apps, or on separate ports for cross-domain apps. In this chapter, we will do the single-domain integration and then we will guide you through the cross-domain integration in Chapter 12, Creating User Logins and API Authentication. We will use the Koa skeleton that we have developed in the previous section for these two types of integration. The single-domain integration requires some configurations in the following steps. Let's get started:

  1. Create a /server/ directory in the Nuxt project's root and structure the server-side directory as follows after creating the project with the create-nuxt-app scaffolding tool:
├── package.json
├── nuxt.config.js
├── server
│ ├── config
│ │ └── ...
│ ├── public
│ │ └── ...
│ ├── static
│ │ └── ...
│ └── index.js
└── pages
└── ...
  1. Modify the default scripts to use Backpack in the default package.json file that comes with the scaffolding tool as follows:
// package.json
"scripts": {
"dev": "backpack",
"build": "nuxt build && backpack build",
"start": "cross-env NODE_ENV=production node build/main.js",
"generate": "nuxt generate"
}
  1. Create a Backpack config file in the root directory (where we have the Nuxt config file) for changing the Backpack default entry directory to the /server/ directory we just created:
// backpack.config.js
module.exports = {
webpack: (config, options, webpack) => {
config.entry.main = './server/index.js'
return config
}
}
  1. Create an index.js file in the /server/ directory to import Koa (make sure you have Koa installed already) as the main app and Nuxt as middleware in Koa as follows:
// server/index.js
import Koa from 'koa'
import consola from 'consola'
import { Nuxt, Builder } from 'nuxt'
const app = new Koa()
const nuxt = new Nuxt(config)

async function start() {
app.use((ctx) => {
ctx.status = 200
ctx.respond = false
ctx.req.ctx = ctx
nuxt.render(ctx.req, ctx.res)
})
}
start()

Notice that we create an async function to use Nuxt as middleware so that we can use the await statement in the next step for running the Nuxt build process.

Note that Consola is a console logger and you must install it via npm before using it. For more information about this package, please visit https://github.com/nuxt-contrib/consola.
  1. Before registering Nuxt as the middleware, import the Nuxt configuration for the build process in development mode:
// server/index.js
let config = require('../nuxt.config.js')
config.dev = !(app.env === 'production')

if (config.dev) {
const builder = new Builder(nuxt)
await builder.build()
} else {
await nuxt.ready()
}
  1. Run the app by listening to its port and host and log the server status with Consola as follows:
app.listen(port, host)
consola.ready({
message: `Server listening on http://${host}:${port}`,
badge: true
})
  1. Launch the app in development mode:
$ npm run dev

Our Nuxt and Koa apps are now running as a single app. You probably have realized that Nuxt is now running under Koa as a middleware. All our Nuxt pages are still running the same as before at localhost:3000, but we will configure localhost:3000/api as the API main endpoint in the coming section.

Adding routes and other essential middleware

We established the integration and structured the server-side directory in the previous section. Now let's refine some API routes and other middleware on our API in the following steps:

  1. Install the Koa Router and Koa Static packages via npm:
$ npm i koa-route
$ npm i koa-static
  1. Create a server-side config file:
// server/config/index.js
export default {
static_dir: {
root: '../static'
}
}
  1. Create a routes.js file in the /server/ directory for defining routes that we will expose to the public with some dummy user data:
// server/routes.js
import Router from 'koa-router'
const router = new Router({ prefix: '/api' })

const users = [
{ id: 1, name: 'Alexandre' },
{ id: 2, name: 'Pooya' },
{ id: 3, name: 'Sébastien' }
]

router.get('/', async (ctx, next) => {
ctx.type = 'json'
ctx.body = {
message: 'Hello World!'
}
})

router.get('/users', async (ctx, next) => {
ctx.type = 'json'
ctx.body = users
})

router.get('/users/:id', async (ctx, next) => {
const id = parseInt(ctx.params.id)
const found = users.find(function (user) {
return user.id == id
})
if (found) {
ctx.body = found
} else {
ctx.throw(404, 'user not found')
}
})
  1. Import other middleware in a separate middlewares.js file and import the routes and config files from steps 1 and 2:
// server/middlewares.js
import serve from 'koa-static'
import bodyParser from 'koa-bodyparser'
import config from './config'
import routes from './routes'

export default (app) => {
app.use(serve(config.static_dir.root))
app.use(bodyParser())
app.use(routes.routes(), routes.allowedMethods())
}

We will not use koa-favicon in the API because we are exporting our data in JSON format and the image of favicon.ico will not be shown on the browser tab. Besides, Nuxt has already handled favicon.ico for us in the Nuxt config file, so we can remove the koa-favicon middleware from our skeleton. Instead, we will create a middleware to decorate our JSON data into these two final JSON outputs

  • The format for 200 output:
{"status":<status code>,"data":<data>}
  • The format for all error outputs (for example, 400, 500):
{"status":<status code>,"message":<error message>}
  1. Add the following code just before the app.use(serve(config.static_dir.root)) line to create the preceding formats:
app.use(async (ctx, next) => {
try {
await next()
if (ctx.status === 404) {
ctx.throw(404)
}
if (ctx.status === 200) {
ctx.body = {
status: 200,
data: ctx.body
}
}
} catch (err) {
ctx.status = err.status || 500
ctx.type = 'json'
ctx.body = {
status: ctx.status,
message: err.message
}
ctx.app.emit('error', err, ctx)
}
})

So now, with this middleware, instead of getting an output such as {"message":"Hello World!"}, we will get the decorated output as follows:

{"status":200,"data":{"message":"Hello World!"}}
  1. Import this middlewares.js file in the main index.js file before registering Nuxt:
// server/index.js
import middlewares from './middlewares'

middlewares(app)
app.use(ctx => {
...
nuxt.render(ctx.req, ctx.res)
})
  1. Rerun the app in development mode:
$ npm run dev
  1. Then, if you visit the app at localhost:3000/api, you will get the following output on the screen:
{"status":200,"data":{"message":"Hello World!"}}

If you visit the user index page at localhost:3000/api/users, you will get the following output on the screen:

{"status":200,"data":[{"id":1,"name":"Alexandre"},{"id":2,"name":"Pooya"},{"id":3,"name":"Sébastien"}]}

You also can use localhost:3000/api/users/<id> to get a specific user. For example, if you use /api/users/1, you will get the following output on the screen:

{"status":200,"data":{"id":1,"name":"Alexandre"}}
You can find this integrated example app in /chapter-8/nuxt-universal/skeletons/koa/ in our GitHub repository.

Next, we will look at how we can request the preceding API data with the asyncData method on the client side from the Nuxt pages in the coming section.

Understanding async data

The asyncData method allows us to fetch data asynchronously and render it on the server side before the component is initiated. It is an additional method that's only available in Nuxt. That means you can't use it in Vue because Vue does not have this default method. Nuxt always executes this method before rendering the page component. It is executed once on the server side on the page that uses this method and then will be executed on the client side when revisiting that page through the routes generated with the <nuxt-link> component. Nuxt will merge the returned data from the asyncData method with the component data from the data method or the data property. This method receives the context object as the first argument, as follows:

export default {
asyncData (context) {
// ...
}
}

Bear in mind that this method is always executed before the page component is initiated, so we have no access to the component instance through the this keyword inside this method. There are two different ways of using it; let's explore them in the upcoming sections.

Returning a promise

We can use the Promise object in the asyncData method by returning Promise, for example:

// pages/returning-promise.vue
asyncData (context) {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello World by returning a Promise')
}, 1000)
})

return promise.then((value) => {
return { message: value }
})
}

In the preceding code, Nuxt will wait for 1 second for the promise to be resolved before rendering the page component with 'Hello World by returning a Promise'.

Using async/await

We also can use an async/await statement with the asyncData method, for example:

// pages/using-async.vue
async asyncData (context) {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('Hello World by using async/await')
}, 2000)
})

const result = await promise
return { message: result }
}

In the preceding code, Nuxt will wait for 2 seconds for the promise to be resolved before rendering the page component with the 'Hello World by using async/await' message. Using the async/await statement is a new way of writing asynchronous JavaScript code. It is built on top of the Promise object and makes our asynchronous code more readable. We will use this statement often throughout the book.

Merging the data

As we mentioned before, the asynchronous data from the asyncData method will be merged with the component data from the data method or the data property. That means that if you have set some default data in your component data with the same object keys in the asyncData method, they will be overwritten by the asyncData method as a result. Here's an example:

// pages/merging-data.vue
<p>{{ message }}</p>

export default {
data () {
return { message: 'Hello World' }
},
asyncData (context) {
return { message: 'Data Merged' }
}
}

In the preceding code, Nuxt will merge the two sets of data and you will get the following result on your screen:

<p>Data Merged</p>
You can find the examples in /chapter-8/nuxt-universal/koa-nuxt/understanding-asyncdata/ in our GitHub repository.

Next, we will look at how we can make use of the context object that we can access from the asyncData method in the coming section.

Accessing context in asyncData

We can access a bunch of useful stuff from the Nuxt context for fetching data. They stored inside the context object as the following keys:

  • app
  • route
  • store
  • params
  • query
  • req
  • res
  • redirect
  • error
  • env
  • isDev
  • isHMR
  • beforeNuxtRender(fn)
  • from
  • nuxtState

 

They are provided additionally and especially in Nuxt only, so we won't find them in Vue. We can access them either with context.<key> or { <key> }. So let's explore some of these keys and see how we can leverage them in the following sections.

For more information about the Nuxt context, please visit https://nuxtjs.org/api/context

Accessing the req/res objects

We can access the req and res objects when the asyncData method is executed on the server side. They contain useful information of the HTTP request sent from the user. But we should always check with an if condition before accessing them:

// pages/index.vue
<p>{{ host }}</p>

export default {
asyncData ({ req, res }) {
if (process.server) {
return { host: req.headers.host }
}
return { host: '' }
}
}

In the preceding code, we use the if condition to make sure that the asyncData method is called on the server side before obtaining the information of the request headers. These two objects are unavailable on the client side, so you will get undefined when accessing them on the client side. So the result we will get from the preceding code is localhost:3000 when the page is loaded on the browser for the first time, but you will not see that piece of information again when revisiting this page by the route generated from the <nuxt-link> component unless you refresh that page.

Accessing the dynamic route data

We can access the dynamic route data through the params key when we have dynamic routes in our app. For example, if we have an _id.vue file in the /pages/ directory, then we can access the value of the route parameter via context.params.id as follows:

// pages/users/_id.vue
<p>{{ id }}</p>

export default {
asyncData ({ params }) {
return { id: params.id }
}
}

In the preceding code, you will get 1 for the id when calling users/1 on the browser.

Listening to the query changes

By default, the asyncData method is not executed over changes on the query string. For example, if you are using queries such as /users?id=<id> on your route with the <nuxt-link> component, asyncData will not be called when changing from one query to another through the <nuxt-link> component routes. This is because watching the query changes is disabled by default in Nuxt to improve performance. If you want to override this default behavior, you can use the watchQuery property to listen to the specific parameters:

// pages/users/index.vue
<p>{{ id }}</p>
<ul>
<li>
<nuxt-link :to="'users?id=1'">1</nuxt-link>
<nuxt-link :to="'users?id=2'">2</nuxt-link>
</li>
</ul>

export default {
asyncData ({ query }) {
return { id: query.id }
},
watchQuery: ['id']
}

In the preceding code, we are listening to the id parameter, so you will get 1 for navigating to /users?id=1 and 2 for /users?id=2. If you want to set up a watcher for all query strings, just simply set watchQuery to true.

Handling errors

We can use the error method from the context object to call the Nuxt default error page and display the error. You can pass the error code and message through the default params.statusCode and params.message properties:

// pages/users/error.vue
export default {
asyncData ({ error }) {
return error({
statusCode: 404,
message: 'User not found'
})
}
}

If you want to change the default properties that you pass to the error method, you can create a custom error page, which you learned about in Chapter 4, Adding Views, Routes, and Transitions. Let's create these custom error properties and layout in the following steps:

  1. Create a page that you want to throw the custom properties to:
// pages/users/error-custom.vue
export default {
asyncData ({ error }) {
return error({
status: 404,
text: 'User not found'
})
}
}
  1. Create a custom error page in the /layouts/ directory:
// layouts/error.vue
<template>
<div>
<h1>Custom Error Page</h1>
<h2>{{ error.status }} Error</h2>
<p>{{ error.text }}</p>
<nuxt-link to="/">Home page</nuxt-link>
</div>
</template>

<script>
export default {
props: ['error'],
layout: 'layout-error'
}
</script>
  1. Create a custom layout page for this error page:
// layouts/layout-error.vue
<template>
<nuxt />
</template>

You should see the custom properties and layout when visiting /users/error-custom.

You can see all the examples in /chapter-8/nuxt-universal/koa-nuxt/accessing-context/ in our GitHub repository.

Next, we will look at how we can use Axios, an HTTP client, with the asyncData method for requesting API data in the coming section.

Fetching async data with Axios

We have created a simple API with Koa and exposed some public routes for its data being accessed, such as /api/users and /api/users/1. We also have integrated this API with Nuxt into a single app in which Nuxt performs as middleware. You have also learned how the asyncData method works and how we can make use of the Nuxt context. Now, let's bring all these three parts together in the final step by using Axios with the asyncData method for requesting the API data.

Installing and configuring Axios

Axios is a promised-based HTTP client for Node.js apps. We worked with vanilla promises with the asyncData method in the previous section. We can simplify our code further and save some lines with Axios, which is powered by asynchronous JavaScript and XML (AJAX) to make asynchronous HTTP requests. Let's get it started in the following steps:

  1. Install Axios via npm:
$ npm i axios

We should always use a full path when making HTTP requests with Axios:

axios.get('https://jsonplaceholder.typicode.com/posts')

But it can be repetitive to include https://jsonplaceholder.typicode.com/ in the path for every request. Besides, this base URL can change over time. So we should abstract it and simplify the request:

axios.get('/posts')
  1. Create an Axios instance in the /plugins/ directory:
// plugins/axios-api.js
import axios from 'axios'

export default axios.create({
baseURL: 'http://localhost:3000'
})
  1. Import this plugin whenever we need it on the component:
import axios from '~/plugins/axios-api'

After this installation and configuration, we are ready to fetch the async data in the next section.

Fetching data with Axios and asyncData

Let's create the pages that need to have the data rendered in the following steps:

  1. Create an index user page to list all users:
// pages/users/index.vue
<li v-for="user in users" v-bind:key="user.id">
<nuxt-link :to="'users/' + user.id">
{{ user.name }}
</nuxt-link>
</li>

<script>
import axios from '~/plugins/axios-api'
export default {
async asyncData({error}) {
try {
let { data } = await axios.get('/api/users')
return { users: data.data }
} catch (e) {
// handle error
}
}
}
</script>

On this page, we use the get method from Axios to call the API endpoint of /api/users, which will be transformed to localhost:3000/api/users, where the list of users can be output as follows:

{"status":200,"data":[{"id":1,"name":"Alexandre"},{"id":2,"name":"Pooya"},{"id":3,"name":"Sébastien"}]}

We then unpack the data key in the output by using JavaScript's destructuring assignment with { data }. It is a good practice to wrap your code in try/catch blocks when using the async/await statements. Next, we will need to request a single user's data.

  1. Create a single user page for rendering individual user data:
// pages/users/_id.vue
<h2>
{{ user.name }}
</h2>

<script>
import axios from '~/plugins/axios-api'
export default {
name: 'id',
async asyncData ({ params, error }) {
try {
let { data } = await axios.get('/api/users/' + params.id)
return { user: data.data }
} catch (e) {
// handle error
}
}
}
</script>

On this page, again, we use the get method from Axios to call the API endpoint of /api/users/<id>, which will be transformed to localhost:3000/api/users/<id>, to fetch the data of a single user:

{"status":200,"data":{"id":1,"name":"Alexandre"}}

And again, we unpack the data key in the output by using JavaScript's destructuring assignment with { data } and wrap the async/await code in try/catch blocks.

In the next section, we want to achieve the same result as in this section, that is, to fetch a list of users and the data of a specific user. But we will do it on a single page with the watchQuery property, which you learned about in the previous section.

Listening on the query change

In this section, we will create a page for listening to the change in the query string and fetching the single-user data. To do this, we only require a .vue page to list all users and watch the query, and if there is any change in the query, we will get the id from the query and fetch the user with that id using Axios in the asyncData method. Let's get started:

  1. Create a users-query.vue page in the /pages/ directory and add the following template to the <template> block:
// pages/users-query.vue
<ul>
<li v-for="user in users" v-bind:key="user.id">
<nuxt-link :to="'users-query?id=' + user.id">
{{ user.name }}
</nuxt-link>
</li>
</ul>
<p>{{ user }}</p>

In this template, we use the v-for directive to loop through each user in users and add the query of each user to the <nuxt-link> component. The data of an individual user will be rendered inside the <p> tag after the <ul> tag.

  1. Add the following code to the <script> block:
// pages/users-query.vue
import axios from '~/plugins/axios-api'

export default {
async asyncData ({ query, error }) {
var user = null
if (Object.keys(query).length > 0) {
try {
let { data } = await axios.get('/api/users/' + query.id)
user = data.data
} catch (e) {
// handle error
}
}

try {
let { data } = await axios.get('/api/users')
return {
users: data.data,
user: user
}
} catch (e) {
// handle error
}
},
watchQuery: true
}

This piece of code is the same as /pages/users/index.vue; we only add a query object to asyncData and fetch the user data based on the information in the query. And, of course, we add watchQuery: true or watchQuery: ['id'] to watch the change in the query. So, in the browser, when you click a user from the list, such as users-query?id=1, the data of that user will be rendered inside the <p> tag as follows:

{ "id": 1, "name": "Alexandre" }

Well done! You have reached the end of this chapter. We hope that it was a simple and easy chapter for you to follow. Besides using Axios to make HTTP requests to the API backend, we can use one of these Nuxt modules: Axios and HTTP. We focus on vanilla Axios and the Axios module in this book. Do you remember that we covered the Axios module in Chapter 6Writing Plugins and Modules? We will use this module often in the coming chapters. Now, let's summarize what you have learned in this chapter.

You can find the preceding code in /chapter-8/nuxt-universal/koa-nuxt/using-axios/axios-vanilla/ in our GitHub repository. If you want to find out more about the Nuxt HTTP module, please visit https://http.nuxtjs.org/.

Summary

In this chapter, you have learned how to configure Nuxt with a server-side framework, which is Koa in this book. You have installed Koa with the dependencies that we need to create an API. And then you used asyncData and Axios to query and fetch the data from the API. Also, you learned about the properties in the Nuxt context that you can destructure and access from the asyncData method, such as params, query, req, res, and error. Last but not least, you started using Backpack as a minimalist build tool in your apps.

In the next chapter, you will learn how to set up MongoDB and write some basic MongoDB queries, how to add data to a MongoDB database, how to integrate it with the server-side framework, Koa, which you have just learned about in this chapter, and then, finally, how to integrate it with Nuxt pages. We will guide you through everything that you will have to learn in order to make a more complete API. So, stayed tuned.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.138.113.188