Jose Navarro is a full stack developer at FAMOCO in Brussels, Belgium. He has been working for the last 3 years as a web developer with Node.js, Java, AngularJS, and ReactJS, and has deep interest in web development and mobile technologies.

Introduction
We are going to develop a REST API using Node.js and Couchbase ODM Ottoman. There are a few frameworks to do this in Node.js, so we are going to use hapi.js, which makes it easy to start and develop an API, and its code is clean and easy to understand. It also provides a validator in the request so we can integrate well with the Ottoman model, which we are going to use so we can abstract our code and work with objects.
RequirementsTo build the project, you need to have the following installed on your computer:
Node.js and NPM
Couchbase Server
Hapi ServerFirst, we create the main directory for our project, then go inside that directory and start the npm project, where we will be asked for a few parameters for our project.
We can do it with the following commands:
mkdir node - hapi - couchbase - api
npm init
The next step is to add the dependencies to our project. First, we will add the hapi.js related packages, then we add the Couchbase related packages, and finally we add nodemon to our dev dependencies for the live reload of our server while we are coding.
npm install - S hapi joi
npm install - S couchbase ottoman
npm install - D nodemon
Once all this is ready, we start to create our project. We create a folder src where we will have all our code. Inside we create an index.js file where we will have our basic hapi server. There we add the following code:
const Hapi = require ( 'hapi' );
// Create a server with a host and port
const server = new Hapi . Server ();
server . connection ({
host : 'localhost',
port : 5000,
routes : {
cors : true,
}
});
// Start the server
server . start ( err => {
if ( err ) {
// Fancy error handling here
console . error ( err );
throw err;
}
console . log ( `Server started at ${ server.info.uri }` );
} );
module . exports = server;
We have just created our basic server.
Now we are going to define an entry route for our server. First, we create a folder API where we will define our routes. And we create a file index.js , with the code of our entry route:
const routes = [{
method : 'GET',
path : '/',
config : {
handler : ( request , reply ) => {
return reply ({
name : 'node-hapi-couchbase-api',
version : 1
});
}
}
}
];
module . exports = routes;
In the main index.js file, we are going to import the routes. For that we add the following code before the server.start code that we defined earlier:
const routes = require ( './api' );
// Add the routes
server . route ( routes );
Now in our package.json file, we will add the script section.
"scripts" : {
"start" : "nodemon ./src/index.js"
},
If we run npm start , we will start our server. We can check it by going to http://localhost:5000 , and we should get a response.
{ "name" : "node-hapi-couchbase-api" , "version" : 1}
Database ConnectorTo set up the database connector, we are going to create a folder db where we will store the database information and the logic for the connector.
We are going to store the information in the file config.json with the following code:
{
"couchbase" : {
"endpoint" : "localhost:8091",
"bucket" : "api"
}
}
For the connector, we are going to create a file index.js , where we are going to import the config file and the Couchbase library and initialize the connection with the database and the bucket.
let config = require ( './config' );
let couchbase = require ( 'couchbase' );
let endpoint = config . couchbase . endpoint;
let bucket = config . couchbase . bucket;
let myCluster = new couchbase . Cluster ( endpoint , function ( err ) {
if ( err ) {
console . log ( "Can't connect to couchbase: %s" , err );
}
console . log ( 'connected to db %s' , endpoint );
});
let myBucket = myCluster . openBucket ( bucket , function ( err ) {
if ( err ) {
console . log ( "Can't connect to bucket: %s" , err );
}
console . log ( 'connected to bucket %s' , bucket );
});
The next step is to import the Couchbase ODM Ottoman and set it up with the bucket.
let ottoman = require ( 'ottoman' );
ottoman . store = new ottoman . CbStoreAdapter ( myBucket , couchbase );
Finally, we are going to export the bucket and Ottoman so we have access from other files.
module . exports = {
bucket : myBucket,
ottoman : ottoman
};
ModelsNow that we have our basic server running, we are going to define our models with Ottoman. We are going to define two models: one for a User and another for a Post . For that we create a folder called models , and inside we create two js files: user.js and post.js . We can add the validations in the model, but hapi.js offers a validation before handling the route, so we are going to use that to validate the data that we receive from the user before we pass it to our model.
User ModelThe user will have three fields: name , email, and password . We create our user model using the package Ottoman. Our user model contains the following code:
let ottoman = require ( '../db' ). ottoman;
let UserModel = ottoman . model ( 'User' , {
password : 'string',
name : 'string',
email : 'string',
}, {
index : {
findByEmail : {
by : 'email',
type : 'refdoc'
}
}
});
First, we import the Ottoman instance that we initiated in the db connector. After that we start defining our model. The first parameter is the name of our model, in this case ‘User’. The second parameter is the JSON object that contains the name of the field and the type; in our case all the values are string (check O ttoman documentation to see other types). The next parameter is the object that contains the index we want to create. We are going to create an index for the email so we can use that index to query for the user using our model; this will also create a restriction to avoid duplicated emails in our users.
When we create an index we need to call the function ensureIndices to create the indexes internally.
ottoman . ensureIndices ( function ( err ) {
if ( err ) {
return console . error ( 'Error ensure indices USER' , err );
}
console . log ( 'Ensure indices USER' );
});
The last step is to export the model.
module . exports = UserModel;
Post ModelThe post will contain four fields: title and body , the timestamp , and the user .
First, we import the Ottoman instance that we initialized in the db connector, and we also import the User model.
let ottoman = require ( '../db' ). ottoman;
let User = require ( './user' );
let PostModel = ottoman . model ( 'Post' , {
user : User,
title : 'string',
body : 'string',
timestamp : {
type : 'Date',
default : Date . now
}
});
The first parameter is the name of our model, ‘Post’. The second is the JSON object with our field. In this case we define user with the type User that we defined in our previous model; the title and body of type string , and timestamp of type Date . We are going to create a default value with the current timestamp when the object is created.
And finally we export our model.
module . exports = PostModel;
API RoutesWe are going to define our routes for Users and Posts; the basic path we are going to use is /api/v1 . In our index.js file inside API, we are going to import the user routes and the post routes, and we will join them in an array.
const users = require ( './users' );
const posts = require ( './posts' );
...
routes = routes . concat ( users );
routes = routes . concat ( posts );
In both User and Post routes, we are going to define the methods to perform a CRUD operation. For every route, we need to define the method, path, and config. In the config section we provide the handler, that it is the function to perform; and we can also provide a validate function that will be called before we perform the handle function. For validations, we are going to use the Joi package, which we can use to define the schema and validations for the body of the request.
User routesFor users, we are going to use the path /api/v1/users . The first step in our routes file is to import the User model and the joi package.
const User = require ( '../models/user' );
const Joi = require ( 'joi' );
Retrieve the list of users GET /api/v1/usersIn the handle function we are going to use the find function from the User model that allows us to query the db to collect all the documents of type User.
{
method : 'GET',
path : '/api/v1/users',
config : {
handler : ( request , reply ) => {
User . find ({}, ( err , users ) => {
if ( err ) {
return reply ({
status : 400,
message : err . message
}). code ( 400 );
}
return reply ({
data : users,
count : users . length
});
});
}
}
}
We are going to return an object with an array of user and a count with the number of objects inside the array.
Retrieve a User by its id GET /api/v1/users/{id}In this case, we are going to query for a user by its document id, so we are going to use the built-in function getById in our model to retrieve a document from the database.
In this case, we provide a validate object to validate that the param value id is a string.
{
method : 'GET',
path : '/api/v1/users/{id}',
config : {
handler : ( request , reply ) => {
User . getById ( request . params . id , ( err , user ) => {
if ( err ) {
return reply ({
status : 400,
message : err . message
}). code ( 400 );
}
return reply ( user );
});
},
validate : {
params : {
id : Joi . string (),
}
}
}
}
We are going to return the document of the user.
Create a new user POST /api/v1/usersNow we are going to create a new user. The first step is to create the User with the User model and the body of the request.
We provide a validate object to check that payload (body of the request).
{
method : 'POST',
path : '/api/v1/users',
config : {
handler : ( request , reply ) => {
const user = new User ( request . payload );
user . save (( err ) => {
if ( err ) {
return reply ({
status : 400,
message : err . message
}). code ( 400 );
}
return reply ( user ). code ( 201 );
});
},
validate : {
payload : {
password : Joi . string (). alphanum (). min ( 3 ). max ( 30 ). required (),
email : Joi . string (). email (). required (),
name : Joi . string ()
}
}
}
}
We will return the object of the new user created.
Update a user PUT /api/v1/users/{id}Now we are going to update a user. In this case we are first going to retrieve the user document from the database, then we will update the fields, and finally we will save the updated document in the database.
In this case, we provide a validate object where we validate both params and