There are many use-cases for NoSQL databases, but one that I encounter frequently is in the realm of storing user data. A user profile store is great for NoSQL because profiles often need to be flexible and able to accept data changes at any given time. While possible in an RDBMS, it would potentially require more work in maintaining the data with a penalty on performance.

About a year ago, Kirk Kirkconnell had written a high-level example of designing a user profile store with Couchbase titled, User Profile Store: Advanced Data Modeling.

We’re going to expand on these ideas and see how to create a simple user profile store using Node.js and Couchbase Server.

Before we see some code, let’s figure out what we’re trying to accomplish.

When it comes to managing user data, we know that we need a way to create profiles and associate things to those profiles. We need to extend this idea so it follows better practice. When designing our profile store, we shouldn’t store account data such as username and password in the profile itself. We also shouldn’t pass sensitive user data with every request for a user action, but instead use a session that expires.

For these reasons, we want the following API endpoints:

  • POST /account – Create a new user profile with account information
  • POST /login – Validate account information
  • GET /account – Get account information
  • POST /blog – Create a new blog entry associated to a user
  • GET /blogs – Get all blog entries for a particular user

With this information in mind, we can start developing our backend for the user profile store. The assumption is that you’ve already gotten Couchbase Server and Node.js installed and configured.

Creating a Node.js with Express Framework Application

The first order of business is to create a new Node.js application with all the required dependencies. Using the Node Package Manager (NPM), execute the following:

The above commands will initialize a new project and install the dependencies. We’ll be using the Node.js Couchbase SDK with Express Framework. To accept JSON data via POST requests we’ll need the body-parser package. The uuid package will allow us to generate unique keys and bcryptjs will allow us to hash our passwords to deter malicious users.

Let’s bootstrap our application now.

Create an app.js file within the project that contains the following JavaScript code:

The above code includes the downloaded dependencies and then initializes them within the project. Then we are telling the application to connect to a locally running Couchbase instance and open the default Bucket.

Because we’ll be using N1QL, we’ll need an index created within our Bucket. You can create a primary index, but that is never a good idea for production. Instead, create the following index:

The only N1QL query we plan to use will obtain all blog posts for a particular profile id. We’ll get much better performance using a specific index rather than a primary index.

The API that we create with Node.js will be serving endpoints on port 3000.

Saving New Users to the Profile Store

As of right now we don’t have any users in our profile store. Creating an endpoint for accomplishing this job should be our first goal. Before we create the endpoint, let’s figure out what we want to do.

We know that a user profile can have any information describing a user, whether that be addresses, phone numbers, social media information, or other. It is never a good idea to store account credentials with the profile information. For this reason we’ll have a minimum of two documents for every user.

The profile document might be modeled like the following:

In the above, notice that the type is an important indicator which tells us what kind of document this is. The account credentials associated to the profile might be modeled like the following:

The above document also has a type, but it is describing an account, not a profile. A very important indicator here is the pid property. This property should match a particular profile’s id. Essentially we are establishing a document relationship without any database constraints.

So let’s code this.

In the app.js file, include the following JavaScript code:

There is quite a bit happening in the above endpoint so we’ll break it down.

First we are checking to make sure both an email and password exists in the request. Chances are you’ll want to validate more than just those properties, but for simplicity of the demo, those two are fine.

Now we create an account object and profile object based on the data that was sent in the request. The pid that we’re saving into the account object is a unique key. It will also be set as the document key for our profile object. However, the account document will have the email address as the key. In the future, if other account methods are added (alternate email, social login, etc.) we can always associate more documents to a profile.

Rather than saving the password into the account object as plain text, we are hashing it with Bcrypt. For more information on password hashing with Node.js and Couchbase, check out a previous tutorial I wrote on the subject. The password is stripped from the profile object for security.

With the data ready, we can insert it into Couchbase. The goal of the save is to be all or nothing. In other words, we want both the account and profile documents to be created successfully, otherwise roll something back. Depending on the success, return something back to the client.

We could have used N1QL queries for inserting the data, but it is significantly easier to do a CRUD operation in this scenario with no penalty to performance.

Exchanging Sensitive Information with a Session Token

With the user profile and account created, we want the user to be able to sign in and start doing activities that will store data and associate it to them. Again, it is never a good idea to pass the username and password around with every request.

For this reason we want to login and establish a session with the user. The session will be stored in the database, it will reference a particular user profile, and it will expire and be automatically removed from the database.

The session model might look like the following:

This document, like the others, has a different type. Just like with the account document, it has a pid that references a profile.

The code that makes this possible might look like the following:

After validating the incoming data we do an account lookup by the email address. If data comes back for the email, we can compare the incoming password with the hashed password returned in the account lookup. Provided that succeeds, we can start creating a new session for the user.

Unlike the previous insert operation, we are setting a document expiration of an hour. After an hour, if the expiration hasn’t been refreshed, the document will disappear from the database. This is good because it will force the user to sign in again and get a new session. This session token will be passed with every future request instead of the password.

Managing a Particular User with the Session Token

At this point we want to be able to get information about our user profile as well as associate new things to the profile. For this we’ll need to confirm we have authority through the session.

We can confirm the session is valid through a simple Node.js middleware. It might look something like the following:

In the above example we are checking the request for an authorization header. If we have a valid bearer token where the token is the session id, we do a lookup. Remember, the session document has the profile id in it. If the session lookup is successful, we’ll save the profile id in the request, refresh the session expiration, and move through the middleware into the endpoint request.

If the session doesn’t exist, no profile id will be passed and the request will fail.

If we wanted to use the middleware to get information about our profile, we could do the following:

Notice the validate happens first and then the rest of the request. The request.pid was established by the middleware and it will get us a particular profile document for that id.

Now maybe we want to create a blog article as this user. We might have an endpoint that looks like the following:

Assuming the middleware succeeded, we can create a blog object with a specific type and pid. Then we can save it into the database.

Querying for all blog posts by a particular user isn’t too much different:

Because we need to query by document property rather than document key, we’ll need to use a N1QL query which uses the index we had created previously.

The document type and pid is passed into the query and all the documents for that particular profile are returned.

Not bad right?

Conclusion

You just saw how to create a simple user profile store and session store using Node.js and NoSQL. This is a great followup to Kirk’s high-level explanation of the task in his previous article.

There is room for improvement in this example. As previously mentioned, the account documents could represent a form of login credentials where you could have a document for basic authentication, Facebook authentication, etc., all referring to the same profile document. Instead of using a UUID for the session, it could be a Json Web Token (JWT) or something even more secure instead.

The next steps will be to create a client front-end for this example.

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.