Plugging a Vue.js SPA frontend to an API backend

Reading time ~4 minutes

Vue.js is a great Javascript frontend framework and its documentation is very clear and straight to the point. You can either choose to integrate Vue into an already existing app (the JQuery way) or build a Webpack based Single Page Application (SPA) in the React.js way. Let’s build a basic SPA here connecting to a REST API backend using Node.js, Webpack, Vue Loader, Vue Router, and Axios. Setting up such a project is not that easy in my opinion, so here is a short memo about how to do it on Ubuntu. For your information, here is why we’re using Webpack.

Set Up a Vue.js Project

Install Node.js and npm.

Then use npm to install vue-cli and use vue-cli to install the vue-loader loader for Webpack that enables you to use Vue components mixing HTML, CSS, and JS in a single .vue file. It also installs the whole ecosystem needed for a SPA like vue-router. Last but not list, install Axios we’ll be using for API fetching.

sudo npm install -g vue-cli
vue init webpack vue_project  # Answer various questions here
cd vue_project
npm install
npm install --save axios

More information about the project’s structure here.

Workflow

Dev

Dev is made dead easy thanks to the local web server and the hot reloading feature (web page is modified on the fly when you change source code). Just run:

npm run dev

and start coding.

Note: I personnaly had to face a bug so that hot reloading was broken because of a permission problem (I’m running Ubuntu 17.10). I fixed it thanks to the following command:

echo 100000 | sudo tee /proc/sys/fs/inotify/max_user_watches

Deployment

Once you need to push your app to production run:

npm run build

and your app is now compiled into the dist directory. It’s up to you to decide how you want to deploy it. Personally I’m putting my app into a Docker container running Nginx and make Nginx point to the folder containing my app thanks to the following block:

location / {
    root /my_app;
}

Of course if you already have another service running on port 80 of the same server you’ll have to think about how you want to organize your services and modify your nginx config accordingly.

Plug to API Backend

Everything is happening inside the src folder from now on.

Set Dev and Prod Server Names Once For All

My API development server is running on http://127.0.0.1 while my API production is running on http://api.example.com so in order to avoid changing my config for every deployment I created the following http-constants.js file at the root of the src folder:

import axios from 'axios'

let baseURL

if (!process.env.NODE_ENV || process.env.NODE_ENV === 'development') {
  baseURL = 'http://127.0.0.1/'
} else {
  baseURL = 'http://api.example.com'
}

export const HTTP = axios.create(
  {
    baseURL: baseURL
  })

and then in every .vue file needing Axios, import HTTP instead of Axios:

import {HTTP} from '../http-constants'

HTTP.get(...).then(...).catch(...)

Note: this proxying feature might be done more easily in the config/index.js using the proxyTable directive but for some reason it did not work for me.

Create The App

Let’s create an app called ShowGreetings that gets an Hello World message from an API. The API endpoint is /greetings and returns the following JSON message when sending a GET request:

{message: "Hello World"}

First create the new Vue component called ShowGreetings.vue in src/components:

<template>
  <div>
    <button @click="getGreetings">Get Greetings</button>
    <h1 v-if="greetings"></h1>
    <p class="error" v-if="errorMessage"></p>
  </div>
</template>

<script>
import {HTTP} from '../http-constants'
export default {
  name: 'ShowGreetings',
  data () {
    return {
      greetings: '',
      errors: ''
    }
  },
  methods: {
    getGreetings: function () {
      HTTP.get('/greetings')
        .then(response => {
          this.greetings = response.data.message
        })
        .catch(e => {
          this.errors = e
        })
    }
  }
}
</script>

<style scoped>
.error {
  color: red;
}
</style>

This component tries to fetch the API backend when you click on a button and displays the returned message. If an error is returned, it displays the error.

Now update the router in order to take this new component into account. Here is our new index.js in src/router:

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import ShowGreetings from '@/components/ShowGreetings'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    },
    {
      path: '/show-greetings',
      name: 'ShowGreetings',
      component: ShowGreetings
    }
  ]
})

We created a named route called “ShowGreetings” so we can now refer to the route by its name rather than its path (much more flexible).

Last of all, edit the App.vue component in src so a link to our new component appears on the home page:

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <router-link :to="{ name: 'ShowGreetings'}">Show Greetings</router-link>
    <router-view/>
  </div>
</template>
<script>
export default {
  name: 'App'
}
</script>
<style>
#app {
  font-family: 'Avenir', Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

Here we just added a new router-link tag.

Conclusion

Once you understand how all the layers are interacting, it becomes very easy to build an SPA with Vue.js that can scale from a basic project to a complex project in production.

This whole little project is available on my GitHub if needed.

Existe aussi en français

API Rate Limiting With Traefik, Docker, Go, and Caching

Limiting API usage based on advanced rate limiting rule is not so easy. In order to achieve this behind the NLP Cloud API, we're using a combination of Docker, Traefik (as a reverse proxy) and local caching within a Go script. When done correctly, you can considerably improve the performance of your rate limiting and properly throttle API requests without sacrificing speed of the requests. Continue reading