Remember the tutorial series I wrote in regards to creating a user profile store with Node.js and NoSQL? That tutorial covered a lot of ground, from creating a RESTful API with Node.js, handling user sessions, data modeling, and of course storing data associated to users.

What if we wanted to take the same concepts and apply them with Golang instead of JavaScript with Node.js?

We’re going to see how to develop a user profile store with Golang and Couchbase Server that acts as a modular replacement to the Node.js alternative.

Going forward, we’re going to assume that you have Go installed and configured, as well as Couchbase Server. It is not important if you’ve seen the Node.js version of this tutorial because we’re going to revisit everything.

In case you’re unfamiliar with what a user profile store is or what it does, it is simply a solution for storing information about users and information associated to users. Take for example a blog. A blog might have numerous authors which are technically users. Each author will write content and that content will be associated to the particular user that wrote it. Each author will have their own method of signing into the blog as well.

Because user data models can change so frequently, using a NoSQL database with a flexible storage model is often more effective than an RDBMS alternative. More information on the data modeling aspect can be found in this article, User Profile Store: Advanced Data Modeling, written by Kirk Kirkconnell.

Golang User Management to Create a New Project

We’re going to spend all of our time in a single Go file. Somewhere in your $GOPATH, create a file called main.go.

We’re also going to need a few dependencies, for Couchbase as well as other packages. From the command line, execute the following:

The above dependencies will allow us to communicate with Couchbase Server, generate UUID values, and create a RESTful API with cross origin resource sharing (CORS) handling.

The next step is to throw down some boilerplate code for our project. Open the project’s main.go file and include the following:

In the above, you’ll notice that we’ve created a few data structures to represent our data. We’re going with the idea of a blogging platform.

The application will have five RESTful API endpoints, a validator method for our user sessions, and a global Couchbase variable that will allow us to access our open instance anywhere in the application.

Notice that the above three endpoints have the Validate function attached to them. This means that the user must have authenticated and be providing a valid session to progress. In this sense the Validate function acts as a middleware.

Because we plan to query for data, more specifically, blog articles to a particular user, we need to have an index created. Using the web dashboard, Couchbase CLI, or Go application, execute the following:

The above index will allow us to query by a type property as well as a pid property.

Now we can start filling in the holes for each of our API endpoints.

Allowing Users to Register New Information with the Profile Store

Since we have no users in the profile store as of right now, it would make sense to create an endpoint that supports the creation of new users.

It is good practice to never store username and password type credential information with actual user information. For this reason, creating a new user means creating a profile document as well as an account document. The account document will reference the profile document. Both will be modeled after our Go data structures that we saw in the boilerplate code.

In the main.go file, add the following:

There are a few important things happening in the above endpoint code.

First we are accepting JSON data that was send with the POST body from the client request. We are generating a unique id for the profile document and hashing the password for safe keeping with BCrypt.

When it comes to actually saving the data, the user profile will receive a unique id while the account will receive an email address as the id and a reference to the profile id within the document.

By following this approach, the account document could easily be extended into other forms of credentials. For example, the account document could be rebranded as basicauth, and we could have Facebook, Twitter, etc., that reference the profile information.

Implementing a Session Token for Users

Signing into the application via our two documents is a little different. It is never a good idea to pass around the username and password more than is absolutely necessary.

For this reason, it is a good idea to use a session token that represents the user. This token can expire and holds no sensitive information.

Take the following sign-in code for the main.go file:

When the email and password is passed to this endpoint, the account document is retrieved based on the email that was provided. The hashed password inside this document is then compared against the unhashed password.

If the credentials are valid, a session document is created. This session document has a unique key, but references the key of the profile document. An expiration time is also added to the document. When the expiration time passes, the document will be automatically removed from Couchbase without any application or user intervention. This helps secure the account.

With the accounts functional, we need to worry about associating information with the users.

Managing User Information in the Profile Store via a Session Token

When a user tries to do something specific to his or herself, we need to validate that they are who they should be and that the information they are changing is applied to the correct person.

This is where the session token validation middleware comes into play.

Every request to one of our three special endpoints will require an authorization header that contains a bearer token with the session id. No bearer token means the request will fail. Incorrect or expired bearer token means the request will fail.

Validation will exchange the session id for a profile id to be used in the next step of the request.

Starting simple, let’s say we want to return the profile information for a particular user. Our endpoint might look like the following:

The pid is passed from the validation middleware and a lookup is done with the profile id.

Not so difficult so far right?

Let’s kick it up a notch and introduce some N1QL queries into our project. Let’s say we want to get all blog posts for a particular user. This will make use of our index as well as the SQL-like queries.

In the above endpoint code, we take the pid from the validation middleware and add it as a parameter for our parameterized query.

We’ll iterate through the QueryResults returned from the query and add them to a []Blog variable. If no results were found, we can just return an empty slice.

Doing direct lookups based on key will always be faster than N1QL queries, but N1QL queries are very useful when you need to query by property information.

The final endpoint isn’t any different than we’ve already seen:

In the above code, we accept a POST body from the client as well as the pid from the validation middleware. That information is then saved into Couchbase.

Conclusion

You just saw how to create a basic user profile store, or in this case, a blogging platform, using the Go programming language and Couchbase Server. This is an alternative tutorial to a previous tutorial that I had written on the same subject, but with Node.js.

Want to take this tutorial to the next level? Check out how to create a web client front-end with Angular or a mobile client front-end with NativeScript.

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

Author

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.

Leave a reply