Error Handling

As described in Introduction, if an error occurs during server-side rendering, we have two coping strategies: one is to fall back to the SPA mode, and the other is to display a custom error page.

Fall back to SPA mode

Automatic fallback

This is the default behavior of Vapper. When any error occurs during server rendering, Vapper will fall back to SPA mode, which will send the SPA page to the client. If the error is an error that only occurs on the server side, or if the error is a non-fatal error, that means the user can continue to use our app. This makes sense in some scenarios, such as ordering page, payment page, and other scenarios that emphasize conversion rates.

Handling errors in routing guards manually

Under normal circumstances, once an error occurs, vapper will automatically fall back to SPA mode, but only if vapper can catch the error. However, when an asynchronous chain breaks, these errors are not captured by vapper, such as errors in routing guards:

router.beforeEach((to, from, next) => {
  if (to.path === '/bar') {
    throw Error('error in the routing guard')
  }
})

In order for vapper to catch errors in the routing guard, we need to manually try...catch the code inside the routing guard and call next(err), as shown in the following code:








 



router.beforeEach((to, from, next) => {
  try {
    if (to.path === '/bar') {
      throw Error('error in the routing guard')
    }
    next()
  } catch (e) {
    next(e)
  }
})

Manually fallback

If you choose Custom Server, and you might write your own business middleware, but Vapper can't catch exceptions thrown by user-written business middleware. So Vapper exposes the vapper.fallbackSPA(req, res) function to manually fallback to the SPA mode so that the user can call this method in their own error handling middleware to manually fallback to SPA mode:

The vapper.fallbackSPA() function takes two parameters: the Nodejs native request object req and the response object res. The following is an example of Koa, showing how to manually fallback to SPA mode when an error occurs.

















 
 
 
 
 
 
 
 
 
 
 
 
 
 












const Koa = require('koa')
const app = new Koa()
const Vapper = require('@vapper/core')

async function starter () {
  const vapper = new Vapper({ mode: process.env.NODE_ENV || 'production' })

  const {
    options: {
      port,
      host
    }
  } = vapper

  await vapper.setup()

  // Your error handling middleware
  app.use(async (ctx, next) => {
    try {
      await next()
    } catch (err) {
      // Manually call the vapper.fallbackSPA() function
      ctx.status = 200
      ctx.respond = false
      vapper.fallbackSPA(ctx.req, ctx.res)
    }
  })

  // Business middleware is written here
  // app.use(...)

  app.use((ctx) => {
    ctx.status = 200
    ctx.respond = false
    vapper.handler(ctx.req, ctx.res)
  })

  app.listen(port, host, () => vapper.logger.info(`Server running at: http://${host}:${port}`))
}

starter()

About how to customize Server Please read: Custom Server.

Custom fallback logic

By default, vapper internally uses serve-static to provide a static resource service. When a user request comes in, vapper will provide the file under dist/ as a static resource to the user. You can configure it via configuration#static, all configuration options are: serve-static#options.

In general, this is fine, but we usually have a separate static resource server or CDN, and our nodejs service becomes a server that only serves the dist/index.html file. Since the size of the dist/index.html file is small, we can read the file into memory when the service starts, and file IO will no longer occur when a request comes. To do this, vapper provides the configuration#fallbackSpaHandler option, which allows you to customize the logic for fallback the SPA, an example:

// 1. Read the dist/index.html file generated by the build into memory when the service starts
const spaHTMLContent = fs.readFileSync(path.resolve(__dirname, '../dist/index.html'), 'utf-8')

// vapper.config.js
module.exports = {
  // Other configurations...

  // Custom fallback SPA logic
  fallbackSpaHandler (req, res) {
    // 2. Send the in-memory string directly to the client
    res.setHeader('Content-Type', 'text/html; charset=UTF-8')
    res.end(spaHTMLContent)
  }
}

Custom error page

Of course, if you want the error page to be displayed to the user when the error occurs, it is very simple.

The enableCustomErrorPage option

The core goal of vapper is to fall back to SPA mode whenever an error occurs. If you need to customize the error page, you need to enable the enableCustomErrorPage option in the vapper.config.js file:

// vapper.config.js

module.exports = {
  enableCustomErrorPage: true
}

the ErrorComponent component

After enabling the custom error page, you also need to provide the ErrorComponent component. When an error occurs, the component will be rendered as an error page and displayed to the user:

 








 











// Importing the `ErrorComponent` component
import ErrorComponent from 'ErrorComponent.vue'

// Export factory function
export default function createApp () {
  // 1. Create a router instance
  // ...

  // 2. Create a root component
  const app = {
    ErrorComponent,
    router,
    // This is necessary, it is for vue-meta
    head: {},
    render: h => h(App)
  }

  // 3. return the root component
  return app
}

The ErrorComponent has a props named error:

// ErrorComponent
export default {
  name: 'ErrorComponent',
  props: ['error'],
  render(h) {
    return h('h1', this.error.code + ',' + this.error.message)
  }
}

The error Object

The error object is an Error instance. You can throw an error object anywhere in the project code, the error object will be used as props for the ErrorComponent component. We can add the corresponding code and message on the error object for use within the ErrorComponent component.

Note that error.code will be used for thestatusCode of the server response, and error.message will be used for thestatusMessage of the server response. Here is an example:

  • throw error in route guard:







 
 





router.beforeEach((to, from, next) => {
  try {
    if (to.path === '/bar') {
      const error = Error('error in the routing guard')
      throw error
    }
  } catch (e) {
    e.code = 500
    e.message = 'Internal Server Error'
    next(e)
  }
  next()
})

404 Error

When users access a non-existent route, the content of the error object error is as follows:

error = {
  url: '/foo',
  code: 404,
  message: 'Page Not Found'
}

It can be used directly in ErrorComponent.

Rules for error handling

When an error occurs:

  • If enableCustomErrorPage: false, then fall back to SPA.
  • If enableCustomErrorPage: true, but no ErrorComponent component is provided, then fallback to SPA.
  • If enableCustomErrorPage: true and anErrorComponent component is provided, but an error occurs within the ErrorComponent component, then fallback toSPA.

In other words, you can optionally throw errors in the ErrorComponent component to achieve free switching between the custom error page and fallback to the SPA mode.