If you’ve been keeping up, I’m a huge advocate for Node.js development and the JavaScript development stack. Previously I had written about the Couchbase, Express, AngularJS, and Node.js (CEAN) stack and modernized the backend API with Hapi.js, a popular alternative to Express. I’m a huge fan of Angular, but recently I’ve been exploring the increasingly popular Vue.js framework.

We’re going to see how to create a full stack application using the JavaScript stack consisting of Node.js, Hapi, Vue.js, and Couchbase NoSQL. Given that all starting characters of the technologies are consonants, I won’t make an attempt at giving it an acronym.

The application we build will have a semi-simplistic data model. We’re going to store people information and address information and configure how addresses are related to certain people.

Hapi.js and Vue.js with Couchbase Project

The Node.js with Hapi backend is going to demonstrate the use of N1QL and subdocument mutations within the database. The Vue.js frontend will give us a semi-attractive outlet towards working with our API.

Getting Couchbase Configured for Querying and with Role-Based Access Control

Before we can start developing our RESTful API that communicates with our Couchbase NoSQL database, the database must be properly configured. We’re going to assume that you’ve already installed Couchbase Server 5 or higher.

With Couchbase ready to go, we need to create a Bucket to hold our data. This tutorial will reference a Bucket called example, but it doesn’t really matter as long as you’re consistent.

Couchbase Example Bucket

The Bucket doesn’t need any special configuration for this example.

With the Bucket available, we need to create a user with permission to work with the Bucket. For information on creating role-based access control (RBAC), check out a previous article I wrote titled, Secure Your NoSQL Data with Couchbase Role-Based Access Control. The account will need Data Reader, Data Writer, and Query Select roles. This will allow us to do CRUD operations against the database as well as run N1QL queries.

Finally, we need to prepare an index for N1QL query support.

Couchbase N1QL Primary Index

In a production environment you’ll want to create an index for every query that you wish to run. For our example, we’re going to use a primary index which is a prototyping index.

Execute the following query to create the index:

The primary index will give us the convenience of being able to run any query on our Bucket at the cost of performance. Custom indexes will give much better performance.

At this point in time we can start development.

Developing a Web Backend with Node.js and Hapi Framework

With Couchbase ready to go, we can start developing the Node.js with Hapi application. If you’ve seen my previous article, a lot of the material will carry over. However, this example is going to be a little more thorough in regards to what Node.js and Couchbase can do.

Assuming you have Node.js installed, we need to create a fresh project. From the CLI, execute the following:

The above commands will initialize a new project and install the hapi package for Hapi.js, the joi package for data validation, the couchbase package for interaction with Couchbase, and the uuid package for generating unique strings.

Next, create an app.js file which will hold all our Node.js code. To get the ball rolling, add the following to your project’s app.js file:

The above code will import our dependencies, connect to our Couchbase instance using the information we had specified in the previous step, and configure our Hapi server to operate at http://localhost:3000.

When defining our connection information, we chose to enable cross-origin resource sharing (CORS). This will allow our Vue.js application to communicate with the Node.js application even though they’re operating from different ports. More information on CORS with Hapi can be found in a previous article that I wrote.

At this point we can start defining the endpoint routes to our API.

The goal is to create data for people and addresses within Couchbase. Since no data currently exists for these two categories, it makes sense to start with endpoints that perform data creation.

The above code will create an endpoint that accepts POST requests. Assuming that the JSON payload sent with the request meets the criteria of the validation logic, the handler will be used. A unique id will be generated and the payload will be saved under the created id. Upon success, the payload with the id will be returned back to the client.

Similar logic can be used when creating addresses:

The validation logic for addresses is a bit different, but everything else remains the same. This same logic can be carried over to pretty much any creation endpoint with POST requests.

With documents available in Couchbase, we can make an attempt to query for them. It makes sense to create a specialized query that will find either person documents or address documents. To do this we’ll be using N1QL.

The above route will use a N1QL query that obtains all documents and their document keys as long as they contain a property called type that equals address.

Likewise, we could do the same thing for person documents:

However, this is boring just querying for documents based on their type property. We should probably establish a relationship between the two types.

The idea here is that we create an array within the person documents that contains an id for every address they are associated with. Rather than getting an entire document, creating or updating the array, and then saving again, we’re going to do a subdocument mutation directly on the database.

Take the following endpoint for example:

The above endpoint expects a route parameter and a payload to exist in every request. The route parameter is the id of a person document and the payload will contain an id of an address document.

By using the mutateIn method, we can provide a document to mutate, and a path to the property that should be mutated. In this case the person document will be mutated, and we’ll be adding values to an addresses array within it. If the addresses property doesn’t exist, don’t worry it will be created.

Once the mutation happens, we’re going to pull down the entire document that we mutated and return it to the client.

Now we can do a more exciting N1QL query on our data. Check out this revised endpoint for gathering person documents:

Now we’re doing a subquery. We’re not just doing a simple query because an array of address id values isn’t very useful to us. Instead the subquery loads those id values so that our results have actual addresses in them.

Cool right?

Let’s finish our RESTful API with one last endpoint.

The above endpoint will allow us to return a single particular address based on its document key.

With the API created, you could easily test it with Postman or a similar tool. However, we’re going to create a frontend for it with Vue.js.

Creating a Client Frontend with Vue.js

The idea behind the frontend is that we’ll be making HTTP requests to the backend that we had just created. Most of our work will be in the requests, data binding, and overall attractiveness of our UI.

If you’re new to Vue.js, make sure that you obtain the Vue CLI. In a new directory, execute the following with the Vue CLI:

The above command will start the scaffolding process for Vue.js. Choose no for everything asked as we won’t be using all those features. When it comes to a standalone project (compiler and runtime) versus runtime-only, it doesn’t matter for this example.

When the scaffolding is done, execute the following commands:

The above commands will download all the dependencies necessary for the base project. We do however need a dependency for making HTTP requests with Vue.js. Execute the following from the CLI:

This project will be using the axios package. For more information axios and making HTTP requests with Vue.js, check out a previous tutorial I wrote titled, Consume Remote API Data via HTTP in a Vue.js Web Application.

Within your project you should have an src/App.vue file. For simplicity, this will be a single page and single file application. Ignore any other components that were created with the scaffold.

Before we start adding HTML markup and JavaScript logic, let’s include Bootstrap as our theming framework. Open the project’s index.html file and make it look like the following:

Most of the above was taken from the Bootstrap getting started documentation. Now when we start coding in the src/App.vue file, it will look a little more attractive.

Like with most Vue project files, there is a <template>, <script>, and <style> block. Our <style> block will have nothing special added, so open the project’s src/App.vue file and include the following:

The bulk of our work will be in the other two code blocks. For simplicity, we’re going to start with the logic and then finish with the UI.

Within the project’s src/App.vue file, include the following JavaScript code:

There is a lot happening in the above, so we’re going to break it down.

The first core piece of logic that we add is around data initialization. The data method, will allow us to initialize variables used throughout this particular file.

In the data method, the input object will be bound to a form in the UI. In our case we will have two forms, one for person and one for address. By default, we want the values to be blank. We’re also initializing our list of people and our list of addresses which will be obtained from the Node.js API.

After the variables have been initialized, we need to load some data from the server. This can be done in the mounted method.

When the application loads, a request for people and a request for addresses is made.

This leads us to the list of methods that can be called from the HTML. The createPerson method will take data from the person form and send it to the API for saving. Similarly, the createAddress method will do the same, but with address information.

The linkAddress method is a little different:

The linkAddress method will take both a particular person id and a particular address id and send it to our subdocument API endpoint. When done, it will update the information in the local variables for displaying in the UI.

This brings us to the HTML portion of our application file. Within the <template> block, we should have something like the following:

Most of the HTML above is Bootstrap boilerplate. If you’ve ever worked with Bootstrap before, you’ll know that there is a lot of preparation involved.

Jump into the following section:

Take notice that the input object that we had initialized is now bound to the form elements. This is a two way data binding.

When the form button is pressed for this particular form, the createPerson method is called. The other form element behaves the same.

Within the main part of the UI, we have a list of data retrieved from the server:

We’re listing people, the expanded address information from our server side N1QL query, and radio button interaction for issuing the subdocument request.

All this is done easily with Vue.js because of how the UI is bound to the logic layer.


You just saw how to create a full stack application composed of strictly JavaScript technologies. We used Node.js with Hapi.js framework for the API backend layer and Vue.js framework for our frontend web browser layer. The frontend consumes data from the backend and the backend gets its data from our Couchbase NoSQL database.

Because we made the application very modular, each of the components can be swapped with a different technology. We could switch Node.js to be Java or something else and we could switch Vue.js to be something else like Angular. Want to see how to go full stack with Golang and Angular, check this tutorial I wrote titled, Build a Full Stack Movie Database with Golang, Angular, and NoSQL.

For more information on using Couchbase with Node.js, check out the Couchbase Developer Portal.

Posted by Nic Raboy, Developer Advocate, Couchbase

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in Java, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Apache Cordova. Nic writes about his development experiences related to making web and mobile development easier to understand.