{"id":2076,"date":"2015-07-16T17:00:00","date_gmt":"2015-07-16T17:00:00","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=2076"},"modified":"2025-06-13T23:47:49","modified_gmt":"2025-06-14T06:47:49","slug":"making-a-game-api-server-using-nodejs-revisited","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/","title":{"rendered":"Making a Game API Server Using Node.js: Revisited"},"content":{"rendered":"<p>A few years ago, Brett Lawson made <a href=\"https:\/\/www.couchbase.com\/blog\/game-servers-and-couchbase-nodejs-part-1\/\">a great blog series<\/a> on using Couchbase Server and Node.js for the development a game server framework. Since then, the Node.js SDK for Couchbase has grown significantly from version 1.x to 2.x.<\/p>\n<p>In this article we&#8217;re going to revisit those original three posts and change them to keep up with the latest Node.js and Express Framework standards as well as the latest Node.js SDK version of Couchbase.<\/p>\n<h2>The Prerequisites<\/h2>\n<ul>\n<li>Node.js 0.12<\/li>\n<li>Couchbase Server 4.0<\/li>\n<\/ul>\n<h2>Preparing the Project<\/h2>\n<p>Whether you&#8217;re on Mac, Windows, or Linux, creating a new Express Framework application should be consistent between them. Create a new\u00a0directory called <strong>gameapi-nodejs<\/strong> probably on your Desktop and from your Terminal (Mac \/ Linux) or Command Prompt (Windows), run the following command:<\/p>\n<pre><code class=\"language-bash\">\r\nnpm init\r\n<\/code><\/pre>\n<p>Answer the questions asked to the best of your ability. Of course, the project directory must be your current directory in the Command Prompt or Terminal for this to be successful. Alternative to the command, you could manually create and populate this file. Create a new file called package.json in your project&#8217;s directory and fill it with the following:<\/p>\n<pre><code class=\"language-json\">\r\n{\r\n  \"name\": \"gameapi-nodejs\",\r\n  \"version\": \"1.0.0\",\r\n  \"description\": \"An example of using the Node.js SDK for Couchbase to create a simple game server\",\r\n  \"author\": \"Couchbase, Inc.\",\r\n  \"license\": \"MIT\"\r\n}\r\n<\/code><\/pre>\n<p>We&#8217;re not done. We need to install the project dependencies before we can start planning out this application. From the Command Prompt or Terminal, run the following command:<\/p>\n<pre><code class=\"language-bash\">\r\nnpm install body-parser couchbase express node-forge uuid --save\r\n<\/code><\/pre>\n<p>This will install Express Framework, the Couchbase Node.js SDK, Forge for password hashing, UUID for generating unique values, and the body-parser middleware for handling URL encoded and JSON POST data.<\/p>\n<h2>Preparing the Database<\/h2>\n<p>Before we start coding, Couchbase Server must be installed with a bucket called <strong>gaming-sample<\/strong> created.<\/p>\n<p>Since this project is going to make use of a major feature of Couchbase 4.0, we&#8217;ll need to further configure the bucket to have a primary index created. If you&#8217;re using a Linux, Mac or Windows machine, this can be easily accomplished through the Couchbase Query (CBQ) client.<\/p>\n<h3>Mac<\/h3>\n<p>To open CBQ on Mac, from the Terminal, run the following:<\/p>\n<pre><code class=\"language-bash\">\r\n.\/Applications\/Couchbase Server.app\/Contents\/Resources\/couchbase-core\/bin\/cbq\r\n<\/code><\/pre>\n<h3>Windows<\/h3>\n<p>To open CBQ on Windows, from the Command Prompt, run the following:<\/p>\n<pre><code class=\"language-bash\">\r\nC:\/Program Files\/Couchbase\/Server\/bin\/cbq.exe\r\n<\/code><\/pre>\n<h3>Creating a Primary Index<\/h3>\n<p>With CBQ open, run the following:<\/p>\n<pre><code class=\"language-sql\">\r\nCREATE PRIMARY INDEX ON `gaming-sample` using GSI;\r\n<\/code><\/pre>\n<p>Your bucket is now ready for use with the rest of the project!<\/p>\n<h2>The Project Structure<\/h2>\n<p>Our project is going to be composed of the following:<\/p>\n<table style=\"width: 500px\">\n<thead style=\"text-align: left\">\n<tr>\n<th style=\"width: 125px\">Item<\/th>\n<th style=\"width: 125px\">Parent<\/th>\n<th style=\"width: 250px\">Description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td>models<\/td>\n<td><\/td>\n<td>All database class files will go in here<\/td>\n<\/tr>\n<tr>\n<td>routes<\/td>\n<td><\/td>\n<td>All API endpoint definitions will go in here<\/td>\n<\/tr>\n<tr>\n<td>accountmodel.js<\/td>\n<td>models<\/td>\n<td>Creating and retrieving account information<\/td>\n<\/tr>\n<tr>\n<td>sessionmodel.js<\/td>\n<td>models<\/td>\n<td>Authenticating users and maintaining session information<\/td>\n<\/tr>\n<tr>\n<td>statemodel.js<\/td>\n<td>models<\/td>\n<td>Create, update, and retrieve game state information<\/td>\n<\/tr>\n<tr>\n<td>routes.js<\/td>\n<td>routes<\/td>\n<td>All endpoints for GET, POST, PUT will be in here<\/td>\n<\/tr>\n<tr>\n<td>app.js<\/td>\n<td><\/td>\n<td>Server setup information<\/td>\n<\/tr>\n<tr>\n<td>config.json<\/td>\n<td><\/td>\n<td>Static variables<\/td>\n<\/tr>\n<tr>\n<td>package.json<\/td>\n<td><\/td>\n<td>Dependency information<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2>The Basics<\/h2>\n<p>Before we get deep into the Node.js game server logic, it is best to get the base Express Framework application configured.<\/p>\n<p>In the project root, create and open a file called <strong>config.json<\/strong> as it will hold static information such as Couchbase connectivity information. Include the following when it is open:<\/p>\n<pre><code class=\"language-json\">\r\n{\r\n    \"couchbase\": {\r\n        \"server\": \"127.0.0.1:8091\",\r\n        \"bucket\": \"gaming-sample\"\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>In the project root, create and open a file called <strong>app.js<\/strong> as it will hold all the basic information about running the Node.js server. Include the following when you&#8217;ve opened the file:<\/p>\n<pre><code class=\"language-javascript\">\r\nvar express = require(\"express\");\r\nvar bodyParser = require(\"body-parser\");\r\nvar couchbase = require(\"couchbase\");\r\nvar config = require(\".\/config\");\r\nvar app = express();\r\n\r\napp.use(bodyParser.json());\r\napp.use(bodyParser.urlencoded({ extended: true }));\r\n\r\n\/\/ Global declaration of the Couchbase server and bucket to be used\r\nmodule.exports.bucket = (new couchbase.Cluster(config.couchbase.server)).openBucket(config.couchbase.bucket);\r\n\r\n\/\/ All endpoints to be used in this application\r\nvar routes = require(\".\/routes\/routes.js\")(app);\r\n\r\nvar server = app.listen(3000, function () {\r\n    console.log(\"Listening on port %s...\", server.address().port);\r\n});\r\n<\/code><\/pre>\n<p>Let&#8217;s break down what we see here. The first few lines are us including the dependencies into our application. Nothing special there. What is important is the following:<\/p>\n<pre><code class=\"language-javascript\">\r\napp.use(bodyParser.json());\r\napp.use(bodyParser.urlencoded({ extended: true }));\r\n<\/code><\/pre>\n<p>This means that we&#8217;re going to be parsing JSON data and URL encoded data from request bodies. In particular POST and PUT requests. The next thing we&#8217;re doing is initializing the Couchbase cluster in the application and opening a single bucket for use within the application:<\/p>\n<pre><code class=\"language-javascript\">\r\nmodule.exports.bucket = (new couchbase.Cluster(config.couchbase.server)).openBucket(config.couchbase.bucket);\r\n<\/code><\/pre>\n<p>By including <strong>module.exports.bucket<\/strong> we&#8217;re saying that we&#8217;re going to use it through the application. Now in other JavaScript files, if we want to access the bucket we can just do:<\/p>\n<pre><code class=\"language-javascript\">\r\nvar bucket = require(\"relative\/path\/to\/app\").bucket;\r\n<\/code><\/pre>\n<p>Next you&#8217;ll see that we include our soon to be created <strong>routes\/routes.js<\/strong> file and passing it the <strong>app\u00a0<\/strong>variable as one of the arguments. What that does will be obvious soon.<\/p>\n<p>Finally, by calling <strong>app.listen<\/strong> we&#8217;re telling Node.js to listen on port 3000 for requests. The application is almost useable in its most basic state. Create and open <strong>routes\/routes.js<\/strong> and add the following lines:<\/p>\n<pre><code class=\"language-javascript\">\r\nvar appRouter = function(app) {\r\n\r\n    app.get(\"\/\", function(req, res) {\r\n        res.status(403).send(\"Not a valid endpoint\");\r\n    });\r\n\r\n}\r\n\r\nmodule.exports = appRouter;\r\n<\/code><\/pre>\n<p>The application can now be run by executing <strong>node app.js<\/strong> from the Command Prompt or Terminal. Landing on\u00a0<strong>https:\/\/localhost:3000<\/strong> should leave you with a &#8220;Not a valid endpoint&#8221; message.<\/p>\n<h2>The API Data Model<\/h2>\n<p>Before diving into the code that matters, it is best to know how the data will look in Couchbase. For any one user, there will four documents that look like the following:<\/p>\n<h3>The User Document<\/h3>\n<p>The user document will hold all information about a user. For this application that information will be a name, username, and password.<br \/>\nThe name of this document will be prefixed with <strong>user::<\/strong> and have a unique uid value appended to it. This document naming strategy makes use of what is called compound keys.<\/p>\n<pre><code class=\"language-json\">\r\n{\r\n    \"type\": \"user\",\r\n    \"uid\", \"\",\r\n    \"name\": \"\",\r\n    \"username\": \"\",\r\n    \"password\": \"\"\r\n}\r\n<\/code><\/pre>\n<h3>The Username Document<\/h3>\n<p>The username document will hold only the uid value that is found in the user document. The purpose of the username document can be thought of like a login method document. For example it could represent simple sign in where the user enters a username and password. Being that the document contains the linking uid, it can be tied to the user document. The username document is prefixed with\u00a0<strong>username::<\/strong> and the actual username is appended to it. A similar strategy can be used if using Facebook or Twitter as login methods and linking them as well through the uid field.<\/p>\n<pre><code class=\"language-json\">\r\n{\r\n    \"type\": \"username\",\r\n    \"uid\": \"\"\r\n}\r\n<\/code><\/pre>\n<h3>The Session Document<\/h3>\n<p>The session document is an auto-expiring document that acts as a users route into more secure information. In theory, the users front-end will store the sid value and pass it between protected endpoints. With it, a uid tied to a user can be accessed.<\/p>\n<pre><code class=\"language-json\">\r\n{\r\n    \"type\": \"session\",\r\n    \"sid\": \"\",\r\n    \"uid\": \"\"\r\n}\r\n<\/code><\/pre>\n<h3>The State Document<\/h3>\n<p>The state document will hold information about the games particular state. For example if the game character has five lives left and twenty potions, that information would get saved here. There is version information to prevent conflicts between two active game sessions from saving under the same account.<\/p>\n<pre><code class=\"language-json\">\r\n{\r\n    \"type\": \"state\",\r\n    \"uid\": \"\",\r\n    \"states\": {\r\n        \"state-name\": {\r\n            \"version\": 1,\r\n            \"data\": { }\r\n        }\r\n    }\r\n}\r\n<\/code><\/pre>\n<h2>Creating The Account Model<\/h2>\n<p>The account model will serve three particular purposes:<\/p>\n<ul>\n<li>Creating a user account<\/li>\n<li>Retreiving a user account<\/li>\n<li>Comparing a hashed password with an unhashed password<\/li>\n<\/ul>\n<p>Before we start coding, we need to get our includes in order. Add the following to the top of the\u00a0<strong>models\/accountmodel.js<\/strong> file:<\/p>\n<pre><code class=\"language-javascript\">\r\nvar uuid = require(\"uuid\");\r\nvar forge = require(\"node-forge\");\r\nvar db = require(\"..\/app\").bucket;\r\nvar N1qlQuery = require('couchbase').N1qlQuery;\r\n<\/code><\/pre>\n<p>Based on the data model seen above, creating a user account will require a name, username, and password. With that information in hand, the password will be hashed, and the user information will be stored along-side a reference document like so:<\/p>\n<pre><code class=\"language-javascript\">\r\nAccountModel.create = function(params, callback) {\r\n    var userDoc = {\r\n        type: \"user\",\r\n        uid: uuid.v4(),\r\n        name: params.name,\r\n        username: params.username,\r\n        password: forge.md.sha1.create().update(params.password).digest().toHex()\r\n    };\r\n    var referenceDoc = {\r\n        type: \"username\",\r\n        uid: userDoc.uid\r\n    };\r\n    db.insert(\"username::\" + userDoc.username, referenceDoc, function(error) {\r\n        if(error) {\r\n            callback(error, null);\r\n            return;\r\n        }\r\n        db.insert(\"user::\" + userDoc.uid, userDoc, function(error, result) {\r\n            if(error) {\r\n                callback(error, null);\r\n                return;\r\n            }\r\n            callback(null, {message: \"success\", data: result});\r\n        });\r\n    });\r\n};\r\n<\/code><\/pre>\n<p>Notice in the above code, we first try to insert a new reference document. If it fails (maybe it already exists), neither documents will be saved and instead the error message will be returned. Whether it is a success or failure, the callback from the routes file is executed for displaying any kind of answer to the requestor.<\/p>\n<p>In terms of reading user data, one of two things could happen. We could either pass in a username because we&#8217;re doing a simple login of-sorts, or we could pass in a user id. Depends on what we&#8217;re after. Let&#8217;s say we&#8217;re just signing in with the username, you&#8217;d probably want to call a function like this:<\/p>\n<pre><code class=\"language-javascript\">\r\nAccountModel.getByUsername = function(params, callback) {\r\n    var query = N1qlQuery.fromString(\r\n        \"select users.* from `gaming-sample` as usernames \" +\r\n        \"join `gaming-sample` as users on keys (\"user::\" || usernames.uid) \" +\r\n        \"where meta(usernames).id = $1\"\r\n    );\r\n    db.query(query, [\"username::\" + params.username], function(error, result) {\r\n        if(error) {\r\n            return callback(error, null);\r\n        }\r\n        callback(null, result);\r\n    });\r\n};\r\n<\/code><\/pre>\n<p>Notice how we&#8217;re using a N1QL query, new in Couchbase 4.0. It is very similar to traditional SQL and it also gives us the convenience of not having to crunch or format data in the application layer. Couchbase Server will do all this for us. We do however, have the option to request data like in older versions of Couchbase and other NoSQL platforms.<\/p>\n<p>In the N1QL statement above, we&#8217;re selecting a document with the compound key appended with our plain text username. A join is happening with the uid property of the usernames document (foreign key) and the users document (primary key).<\/p>\n<p>This brings us to validating a password. No database calls are made here. We&#8217;re simply going to take a raw password, hash it, then compare the hash with the stored password.<\/p>\n<pre><code class=\"language-javascript\">\r\nAccountModel.validatePassword = function(rawPassword, hashedPassword) {\r\n    return forge.md.sha1.create().update(rawPassword).digest().toHex() === hashedPassword ? true : false;\r\n};\r\n<\/code><\/pre>\n<p>To make <strong>models\/accountmodel.js<\/strong> useable in our routes file we must export it like so at the bottom of the<br \/>\n<strong>models\/accountmodel.js<\/strong> code:<\/p>\n<pre><code class=\"language-javascript\">\r\nmodule.exports = AccountModel;\r\n<\/code><\/pre>\n<h2>Creating The Session Model<\/h2>\n<p>The session model will serve three particular purposes:<\/p>\n<ul>\n<li>Creating a user session<\/li>\n<li>Retreiving a user session<\/li>\n<li>Validating a user session<\/li>\n<\/ul>\n<p>Before we start coding, we need to get our includes in order. Add the following to the top of the\u00a0<strong>models\/sessionmodel.js<\/strong> file:<\/p>\n<pre><code class=\"language-javascript\">\r\nvar uuid = require(\"uuid\");\r\nvar db = require(\"..\/app\").bucket;\r\n<\/code><\/pre>\n<h3>Creating A Session<\/h3>\n<p>When a user wishes to create a session, a uid must be provided. With this in hand, a unique session id is created and inserted into the database with an expiration. When the document expires, it will automatically be removed from Couchbase with no user intervention, thus logging the user out.<\/p>\n<pre><code class=\"language-javascript\">\r\nSessionModel.create = function(uid, callback) {\r\n    var sessionDoc = {\r\n        type: \"session\",\r\n        sid: uuid.v4(),\r\n        uid: uid\r\n    };\r\n    db.insert(\"session::\" + sessionDoc.sid, sessionDoc, {\"expiry\": 3600}, function(error, result) {\r\n        if(error) {\r\n            callback(error, null);\r\n            return;\r\n        }\r\n        callback(null, sessionDoc.sid);\r\n    });\r\n};\r\n<\/code><\/pre>\n<h3>Authenticating A User<\/h3>\n<p>With the user session created, the session id must be used every time the user wants to reach a protected endpoint.<\/p>\n<pre><code class=\"language-javascript\">\r\nSessionModel.authenticate = function(req, res, next) {\r\n    if(!req.headers.authorization) {\r\n        next(\"Must be authorized to use\");\r\n    }\r\n    var authInfo = req.headers.authorization.split(\" \");\r\n    if(authInfo[0] === \"Bearer\") {\r\n        var sid = authInfo[1];\r\n        SessionModel.get(sid, function(error, result) {\r\n            if(error) {\r\n                return next(error);\r\n            }\r\n            SessionModel.refresh(sid, function() {});\r\n            req.uid = result.value.uid;\r\n            next();\r\n        });\r\n    }\r\n};\r\n<\/code><\/pre>\n<p>In the above code, the function will receive a session id which it will then use to look up to see if a session already exists. If it does, it will reset the session expiration time and return the uid associated to it. Getting the session information can be seen as follows:<\/p>\n<pre><code class=\"language-javascript\">\r\nSessionModel.get = function(sid, callback) {\r\n    db.get(\"session::\" + sid, function(error, result) {\r\n        if(error) {\r\n            callback(error, null);\r\n            return;\r\n        }\r\n        callback(null, result);\r\n    });\r\n};\r\n<\/code><\/pre>\n<p>Not too bad right? Finally you can see that the session reset happens in a similar fashion:<\/p>\n<pre><code class=\"language-javascript\">\r\nSessionModel.refresh = function(sid, callback) {\r\n    db.touch(\"session::\" + sid, 3600, function(error, result) {\r\n        if(error) {\r\n            callback(error, null);\r\n        }\r\n    });\r\n};\r\n<\/code><\/pre>\n<p>The touch method won&#8217;t add time, it will instead reset time. In this case it will reset the timer to one hour<\/p>\n<p>To make <strong>models\/sessionmodel.js<\/strong> useable in our routes file we must export it like so at the bottom of the\u00a0<strong>models\/sessionmodel.js<\/strong> code:<\/p>\n<pre><code class=\"language-javascript\">\r\nmodule.exports = SessionModel;\r\n<\/code><\/pre>\n<h2>Creating The State Model<\/h2>\n<p>The state model will serve two particular purposes:<\/p>\n<ul>\n<li>Creating or updating a save-state<\/li>\n<li>Retreiving a save-state by name<\/li>\n<\/ul>\n<p>Before we start coding, we need to get our includes in order. Add the following to the top of the\u00a0<strong>models\/statemodel.js<\/strong> file:<\/p>\n<pre><code class=\"language-javascript\">\r\nvar uuid = require(\"uuid\");\r\nvar couchbase = require(\"couchbase\");\r\nvar N1qlQuery = require('couchbase').N1qlQuery;\r\nvar db = require(\"..\/app\").bucket;\r\n<\/code><\/pre>\n<h3>Creating A Save State<\/h3>\n<p>The goal behind creating or updating a save-state is that we will first check to see if one exists. If it does not, we will create it. If it does, then we will get whatever information exists, change it, then replace whatever exists currently in the database. This is all done while increasing the state version to avoid conflicts between game saves. Not really to prevent conflicts in saving to Couchbase, just to make sure you don&#8217;t pick up the game on two devices and override game data with a much older save.<\/p>\n<pre><code class=\"language-javascript\">\r\nStateModel.save = function(uid, name, preVer, data, callback) {\r\n    db.get(\"user::\" + uid + \"::state\", function(error, result) {\r\n        if(error &amp;&amp; error.code !== couchbase.errors.keyNotFound) {\r\n            callback(error, null);\r\n            return;\r\n        }\r\n        var stateDoc = {\r\n            type: \"state\",\r\n            uid: uid,\r\n            states: {}\r\n        };\r\n        if(result != null &amp;&amp; result.value) {\r\n            stateDoc = result.value;\r\n        }\r\n        var stateBlock = {\r\n            version: 0,\r\n            data: null\r\n        };\r\n        if(stateDoc.states[name]) {\r\n            stateBlock = stateDoc.states[name];\r\n        } else {\r\n            stateDoc.states[name] = stateBlock;\r\n        }\r\n        if(stateBlock.version !== preVer) {\r\n            return callback({\"status\": \"error\", \"message\": \"Your version does not match the server version\"});\r\n        } else {\r\n            stateBlock.version++;\r\n            stateBlock.data = data;\r\n        }\r\n        var stateOptions = {};\r\n        if(result != null &amp;&amp; result.value) {\r\n            stateOptions.cas = result.cas;\r\n        }\r\n        db.upsert(\"user::\" + uid + \"::state\", stateDoc, stateOptions, function(error, result) {\r\n            if(error) {\r\n                return callback(error, null);\r\n            }\r\n            callback(null, stateBlock);\r\n        });\r\n    });\r\n};\r\n<\/code><\/pre>\n<p>See the <strong>upsert<\/strong> in there. In Couchbase that means create if it doesn&#8217;t exist, or replace if it does. Very convenient for things like save-states for a game.<\/p>\n<h3>Getting The States<\/h3>\n<p>This leaves us with getting any save-state that we might have created.<\/p>\n<pre><code class=\"language-javascript\">\r\nStateModel.getByUserIdAndName = function(uid, name, callback) {\r\n    db.get(\"user::\" + uid + \"::state\", function(error, result) {\r\n        if(error) {\r\n            if(error.code !== couchbase.errors.keyNotFound) {\r\n                return callback(null, {});\r\n            } else {\r\n                return callback(error, null);\r\n            }\r\n        }\r\n        if(!result.value.states[name]) {\r\n            return callback({\"status\": \"error\", \"message\": \"State does not exist\"}, null);\r\n        }\r\n        callback(null, result.value.states[name]);\r\n    });\r\n};\r\n<\/code><\/pre>\n<p>The concept behind this is that we&#8217;re doing a document lookup based on a user id. If the state document for a particular uid exists then do a lookup on the associative array to see if the state name exists. If it does, return whatever state content exists for the name.<\/p>\n<p>To make <strong>models\/statemodel.js<\/strong> useable in our routes file we must export it like so at the bottom of the\u00a0<strong>models\/statemodel.js<\/strong> code:<\/p>\n<pre><code class=\"language-javascript\">\r\nmodule.exports = StateModel;\r\n<\/code><\/pre>\n<h2>Creating The API Routes<\/h2>\n<p>We&#8217;ve created all the necessary data models above, so it is time to string it all together with user accessible routes. Going back to the <strong>routes\/routes.js<\/strong> file, we&#8217;ll start by adding the account model routes:<\/p>\n<pre><code class=\"language-javascript\">\r\napp.post(\"\/api\/user\", function(req, res) {\r\n    if(!req.body.name) {\r\n        return res.status(400).send({\"status\": \"error\", \"message\": \"A name is required\"});\r\n    } else if(!req.body.username) {\r\n        return res.status(400).send({\"status\": \"error\", \"message\": \"A username is required\"});\r\n    } else if(!req.body.password) {\r\n        return res.status(400).send({\"status\": \"error\", \"message\": \"A password is required\"});\r\n    }\r\n    AccountModel.create(req.body, function(error, result) {\r\n        if(error) {\r\n            return res.status(400).send(error);\r\n        }\r\n        res.send(result);\r\n    });\r\n});\r\n<\/code><\/pre>\n<p>The above endpoint expects a POST request with a name, username, and password body parameter We are listening for POST because it is best practice to use POST when creating or inserting data over an HTTP request. If all three exist, then the\u00a0<strong>AccountModel.create()<\/strong> method is called, finally returning either an error or result depending on how successful the method was. If at least one of the required parameters does not exist, an error is returned. A list of error codes can be seen <a href=\"https:\/\/en.wikipedia.org\/wiki\/List_of_HTTP_status_codes\">here<\/a>.<\/p>\n<p>An endpoint for getting user information isn&#8217;t so important in this example, so we&#8217;ll jump straight into authenticating the user and creating a session. In the <strong>routes\/routes.js<\/strong> file, add the following:<\/p>\n<pre><code class=\"language-javascript\">\r\napp.get(\"\/api\/auth\", function(req, res, next) {\r\n    if(!req.query.username) {\r\n        return next(JSON.stringify({\"status\": \"error\", \"message\": \"A username must be provided\"}));\r\n    }\r\n    if(!req.query.password) {\r\n        return next(JSON.stringify({\"status\": \"error\", \"message\": \"A password must be provided\"}));\r\n    }\r\n    AccountModel.getByUsername(req.query, function(error, user) {\r\n        if(error) {\r\n            return res.status(400).send(error);\r\n        }\r\n        if(!AccountModel.validatePassword(req.query.password, user[0].password)) {\r\n            return res.send({\"status\": \"error\", \"message\": \"The password entered is invalid\"});\r\n        }\r\n        SessionModel.create(user[0].uid, function(error, result) {\r\n            if(error) {\r\n                return res.status(400).send(error);\r\n            }\r\n            res.setHeader(\"Authorization\", \"Bearer \" + result);\r\n            res.send(user);\r\n        });\r\n    });\r\n});\r\n<\/code><\/pre>\n<p>The authentication endpoint expects a username and password. If both are found, the user will be looked up. If the user is found a password comparison is made and if successful then a session will be created.<\/p>\n<p>The final two API endpoints that are useful to us are for getting and creating save states. Starting with creating a save state endpoint, in your\u00a0<strong>routes\/routes.js<\/strong>, add the following:<\/p>\n<pre><code class=\"language-javascript\">\r\napp.put(\"\/api\/state\/:name\", SessionModel.authenticate, function(req, res, next) {\r\n    StateModel.save(req.uid, req.params.name, parseInt(req.query.preVer, 10), req.body, function(error, result) {\r\n        if(error) {\r\n            return res.send(error);\r\n        }\r\n        res.send(result);\r\n    });\r\n});\r\n<\/code><\/pre>\n<p>The above endpoint expects a URL parameter representing the state name, a query parameter representing the current state version, and a request body that can contain any JSON imaginable as it represents the game data worth saving.<\/p>\n<p>Finally, we&#8217;re left with getting states that have been saved.<\/p>\n<pre><code class=\"language-javascript\">\r\napp.get(\"\/api\/state\/:name\", SessionModel.authenticate, function(req, res, next) {\r\n    StateModel.getByUserIdAndName(req.uid, req.params.name, function(error, result) {\r\n        if(error) {\r\n            return res.send(error);\r\n        }\r\n        res.send(result);\r\n    });\r\n});\r\n<\/code><\/pre>\n<p>The above endpoint expects a URL parameter representing the particular save state to find. Of course it also expects the user to be authenticated first as well.<\/p>\n<h2>Testing The API<\/h2>\n<p>The API endpoints we created in the <strong>routes\/routes.js<\/strong> file can be tested a few ways. Two of my favorite ways to test are with the Postman extension for Chrome or with cURL. Try it for yourself using cURL:<\/p>\n<pre><code class=\"language-bash\">\r\ncurl -X POST https:\/\/localhost:3000\/api\/user --data \"name=Nic%20Raboy&amp;username=nraboy&amp;password=12345\"\r\n\r\nRESPONSE:\r\n\r\n{\r\n    \"message\": \"success\",\r\n    \"data\": {\r\n        \"cas\": \"16588775686144\"\r\n    }\r\n}\r\n<\/code><\/pre>\n<p>Above we went ahead and created a new user account<\/p>\n<pre><code class=\"language-bash\">\r\ncurl -X GET \"https:\/\/localhost:3000\/api\/auth?username=nraboy&amp;password=12345\"\r\n\r\nRESPONSE:\r\n\r\n[\r\n    {\r\n        \"name\": \"Nic Raboy\",\r\n        \"password\": \"8cb2237d0679ca88db6464eac60da96345513964\",\r\n        \"type\": \"user\",\r\n        \"uid\":\"c3e834b0-867e-4a43-aede-b15a4e139adc\",\r\n        \"username\": \"nraboy\"\r\n    }\r\n]\r\n<\/code><\/pre>\n<p>Above we went ahead and created a user session. This same strategy can be applied for the other endpoints as well.<\/p>\n<h2>Conclusion<\/h2>\n<p>Using Node.js and the Couchbase Server SDK, you can easily create an API backend for your games. In Couchbase 4.0 you now have the freedom to use N1QL as an option for querying data in your application.<\/p>\n<p>The full Node.js application that was written about in this article can be downloaded for free from the <a href=\"https:\/\/github.com\/couchbaselabs\/gameapi-nodejs\">Couchbase Labs GitHub\u00a0<\/a>repository.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A few years ago, Brett Lawson made a great blog series on using Couchbase Server and Node.js for the development a game server framework. Since then, the Node.js SDK for Couchbase has grown significantly from version 1.x to 2.x. In [&hellip;]<\/p>\n","protected":false},"author":63,"featured_media":13873,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[1822,1812],"tags":[1393],"ppma_author":[9032],"class_list":["post-2076","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-node-js","category-n1ql-query","tag-api"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v26.2 (Yoast SEO v26.2) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Node.js Game Server Framework &amp; Development Architecture<\/title>\n<meta name=\"description\" content=\"This article explains development of the latest Node.js game server and Express Framework standards as well as the latest Node.js SDK version of Couchbase.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Making a Game API Server Using Node.js: Revisited\" \/>\n<meta property=\"og:description\" content=\"This article explains development of the latest Node.js game server and Express Framework standards as well as the latest Node.js SDK version of Couchbase.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/thepolyglotdeveloper\" \/>\n<meta property=\"article:published_time\" content=\"2015-07-16T17:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T06:47:49+00:00\" \/>\n<meta name=\"author\" content=\"Nic Raboy, Developer Advocate, Couchbase\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@nraboy\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Nic Raboy, Developer Advocate, Couchbase\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"14 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/\"},\"author\":{\"name\":\"Nic Raboy, Developer Advocate, Couchbase\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1\"},\"headline\":\"Making a Game API Server Using Node.js: Revisited\",\"datePublished\":\"2015-07-16T17:00:00+00:00\",\"dateModified\":\"2025-06-14T06:47:49+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/\"},\"wordCount\":2387,\"commentCount\":5,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"keywords\":[\"API\"],\"articleSection\":[\"Node.js\",\"SQL++ \/ N1QL Query\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/\",\"name\":\"Node.js Game Server Framework & Development Architecture\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2015-07-16T17:00:00+00:00\",\"dateModified\":\"2025-06-14T06:47:49+00:00\",\"description\":\"This article explains development of the latest Node.js game server and Express Framework standards as well as the latest Node.js SDK version of Couchbase.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png\",\"width\":1800,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Making a Game API Server Using Node.js: Revisited\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"name\":\"The Couchbase Blog\",\"description\":\"Couchbase, the NoSQL Database\",\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png\",\"width\":218,\"height\":34,\"caption\":\"The Couchbase Blog\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1\",\"name\":\"Nic Raboy, Developer Advocate, Couchbase\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/8863514d8bed0cf6080f23db40e00354\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g\",\"caption\":\"Nic Raboy, Developer Advocate, Couchbase\"},\"description\":\"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.\",\"sameAs\":[\"https:\/\/www.thepolyglotdeveloper.com\",\"https:\/\/www.facebook.com\/thepolyglotdeveloper\",\"https:\/\/x.com\/nraboy\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/author\/nic-raboy-2\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Node.js Game Server Framework & Development Architecture","description":"This article explains development of the latest Node.js game server and Express Framework standards as well as the latest Node.js SDK version of Couchbase.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/","og_locale":"en_US","og_type":"article","og_title":"Making a Game API Server Using Node.js: Revisited","og_description":"This article explains development of the latest Node.js game server and Express Framework standards as well as the latest Node.js SDK version of Couchbase.","og_url":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/","og_site_name":"The Couchbase Blog","article_author":"https:\/\/www.facebook.com\/thepolyglotdeveloper","article_published_time":"2015-07-16T17:00:00+00:00","article_modified_time":"2025-06-14T06:47:49+00:00","author":"Nic Raboy, Developer Advocate, Couchbase","twitter_card":"summary_large_image","twitter_creator":"@nraboy","twitter_misc":{"Written by":"Nic Raboy, Developer Advocate, Couchbase","Est. reading time":"14 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/"},"author":{"name":"Nic Raboy, Developer Advocate, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1"},"headline":"Making a Game API Server Using Node.js: Revisited","datePublished":"2015-07-16T17:00:00+00:00","dateModified":"2025-06-14T06:47:49+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/"},"wordCount":2387,"commentCount":5,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","keywords":["API"],"articleSection":["Node.js","SQL++ \/ N1QL Query"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/","url":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/","name":"Node.js Game Server Framework & Development Architecture","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","datePublished":"2015-07-16T17:00:00+00:00","dateModified":"2025-06-14T06:47:49+00:00","description":"This article explains development of the latest Node.js game server and Express Framework standards as well as the latest Node.js SDK version of Couchbase.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","width":1800,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/making-a-game-api-server-using-nodejs-revisited\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Making a Game API Server Using Node.js: Revisited"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"The Couchbase Blog","description":"Couchbase, the NoSQL Database","publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.couchbase.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2023\/04\/admin-logo.png","width":218,"height":34,"caption":"The Couchbase Blog"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1","name":"Nic Raboy, Developer Advocate, Couchbase","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/8863514d8bed0cf6080f23db40e00354","url":"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g","caption":"Nic Raboy, Developer Advocate, Couchbase"},"description":"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.","sameAs":["https:\/\/www.thepolyglotdeveloper.com","https:\/\/www.facebook.com\/thepolyglotdeveloper","https:\/\/x.com\/nraboy"],"url":"https:\/\/www.couchbase.com\/blog\/author\/nic-raboy-2\/"}]}},"authors":[{"term_id":9032,"user_id":63,"is_guest":0,"slug":"nic-raboy-2","display_name":"Nic Raboy, Developer Advocate, Couchbase","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g","author_category":"","last_name":"Raboy","first_name":"Nic","job_title":"","user_url":"https:\/\/www.thepolyglotdeveloper.com","description":"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."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/2076","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/users\/63"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/comments?post=2076"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/posts\/2076\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media\/13873"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/media?parent=2076"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/categories?post=2076"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/tags?post=2076"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/wp-json\/wp\/v2\/ppma_author?post=2076"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}