{"id":1230,"date":"2018-02-08T07:00:16","date_gmt":"2018-02-08T15:00:16","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/"},"modified":"2018-02-08T07:00:16","modified_gmt":"2018-02-08T15:00:16","slug":"use-openwhisk-for-faas-with-node-js-and-couchbase-nosql","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/es\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/","title":{"rendered":"Use OpenWhisk for FaaS with Node.js and Couchbase NoSQL"},"content":{"rendered":"\n<p>If you&#8217;ve been keeping up with my content, you&#8217;ll remember that I had written an article titled,\u00a0<a href=\"https:\/\/www.couchbase.com\/blog\/use-aws-lambda-api-gateway-node-js-couchbase-nosql\/\" target=\"_blank\" rel=\"noopener\">Use AWS Lambda and API Gateway with Node.js and Couchbase NoSQL<\/a>. In this article we had explored using Amazon&#8217;s Serverless services to create Lambda functions that interact with Couchbase, our NoSQL database.<\/p>\n\n\n\n<p>However, Lambda isn&#8217;t the only serverless, otherwise known as functions as a service (FaaS), technology on the block. Take <a href=\"https:\/\/openwhisk.apache.org\/\" target=\"_blank\" rel=\"noopener\">Apache OpenWhisk<\/a> for example. With OpenWhisk you can create functions similarly to how you would with Lambda, but deploy them to a more diverse set of locations, the popular being IBM Bluemix.<\/p>\n\n\n\n<p>We&#8217;re going to see how to create serverless functions using OpenWhisk to communicate with our <a href=\"https:\/\/www.couchbase.com\" target=\"_blank\" rel=\"noopener\">Couchbase Server<\/a> database.<\/p>\n\n\n\n<p><!--more--><\/p>\n\n\n\n<p>Going forward, there are a few things to note. You&#8217;ll need to be hosting Couchbase Server somewhere accessible by the outside world. This means that your local computer won&#8217;t work. You&#8217;re going to need Docker so we can compile our dependencies to work with OpenWhisk. Finally, you&#8217;re going to need a Bluemix account, for this example at least.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Installing the Bluemix CLI Tools for OpenWhisk<\/h2>\n\n\n\n<p>Like I previously mentioned, OpenWhisk is an Apache Foundation project. However, for convenience we&#8217;re going to be using it on IBM&#8217;s Bluemix.<\/p>\n\n\n\n<p>Create an account for the <a href=\"https:\/\/console.bluemix.net\/openwhisk\/\" target=\"_blank\" rel=\"noopener\">IBM Cloud<\/a> if you haven&#8217;t already.<\/p>\n\n\n\n<p>Instead of using a framework tool like Serverless, we&#8217;re going to be using the Bluemix CLI. Download the <a href=\"https:\/\/console.bluemix.net\/openwhisk\/learn\/cli\" target=\"_blank\" rel=\"noopener\">IBM Cloud Functions CLI<\/a> so we can interact with OpenWhisk on IBM.<\/p>\n\n\n\n<p>Before you can start working with your IBM Cloud account, you need to sign in via the CLI. From the command line, execute the following:<\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]bx login -a api.ng.bluemix.net -o your_email@example.com -s dev[\/crayon]<\/p>\n\n\n\n<p>When downloading the CLI, you&#8217;ll be given the exact command, but it should look similar to what I&#8217;ve presented above.<\/p>\n\n\n\n<p>Now we can start creating our project.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Understanding the Project Structure and OpenWhisk Package Creation Process<\/h2>\n\n\n\n<p>If you&#8217;ve never worked with FaaS before, things are done a little differently than building a stand-alone, hardly scalable application.<\/p>\n\n\n\n<p>For example, each endpoint in our FaaS project will be a separate function. Combined, these functions create what is called a package. These functions scale as necessary to meet the changing demand of your application.<\/p>\n\n\n\n<p>With that said, create the following:<\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]create<br \/>\n&#8212; package.json<br \/>\n&#8212; create.js<br \/>\nretrieve<br \/>\n&#8212; package.json<br \/>\n&#8212; retrieve.js<br \/>\nupdate<br \/>\n&#8212; package.json<br \/>\n&#8212; update.js<br \/>\ndelete<br \/>\n&#8212; package.json<br \/>\n&#8212; delete.js[\/crayon]<\/p>\n\n\n\n<p>The project should have a directory for each function that we wish to create. Each function will have its own\u00a0<strong>package.json<\/strong> file. Each\u00a0<strong>package.json<\/strong> file can be created by executing the following within each of the directories:<\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]npm init -y[\/crayon]<\/p>\n\n\n\n<p>Within each of the\u00a0<strong>package.json<\/strong> files, you&#8217;ll also need to define which file is your function code. For example, open\u00a0<strong>create\/package.json<\/strong> and add or change the following line:<\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]&#8221;main&#8221;: &#8220;create.js&#8221;,[\/crayon]<\/p>\n\n\n\n<p>By setting the <code>main<\/code> file, we are stating which JavaScript file contains our function.<\/p>\n\n\n\n<p>When we start deploying our functions, we&#8217;ll be doing it so they are part of the same package.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Designing a Function for Creating Data<\/h2>\n\n\n\n<p>Let&#8217;s start development with creating data in our database. Navigate to the\u00a0<strong>create<\/strong> directory and execute the following command from your command line:<\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]npm install couchbase uuid joi &#8211;save[\/crayon]<\/p>\n\n\n\n<p>The above command will install our function dependencies. We&#8217;ll be using the Couchbase SDK for Node.js, the UUID library for generating unique keys, and the Joi library for validating input.<\/p>\n\n\n\n<p>We will be revisiting the dependency installation, but at least it will keep us going for now.<\/p>\n\n\n\n<p>Now open the project&#8217;s\u00a0<strong>create\/create.js<\/strong> file and include the following:<\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]const Couchbase = require(&#8220;couchbase&#8221;);<br \/>\nconst UUID = require(&#8220;uuid&#8221;);<br \/>\nconst Joi = require(&#8220;joi&#8221;);<\/p>\n<p>var bucket = null;<\/p>\n<p>function main(params) {<br \/>\n    if(bucket == null) {<br \/>\n        var cluster = new Couchbase.Cluster(&#8220;couchbase:\/\/&#8221; + params.host);<br \/>\n        cluster.authenticate(params.username, params.password);<br \/>\n        bucket = cluster.openBucket(params.bucketName);<br \/>\n    }<br \/>\n    var schema = Joi.object().keys({<br \/>\n        firstname: Joi.string().required(),<br \/>\n        lastname: Joi.string().required(),<br \/>\n        type: Joi.string().forbidden().default(&#8220;person&#8221;)<br \/>\n    });<br \/>\n    var data = params;<br \/>\n    var response = {};<br \/>\n    return new Promise((resolve, reject) =&gt; {<br \/>\n        var validation = Joi.validate(data, schema, { stripUnknown: true });<br \/>\n        if(validation.error) {<br \/>\n            response = {<br \/>\n                statusCode: 500,<br \/>\n                body: JSON.stringify(validation.error.details)<br \/>\n            };<br \/>\n            reject(response);<br \/>\n        }<br \/>\n        var id = UUID.v4();<br \/>\n        bucket.insert(id, validation.value, (error, result) =&gt; {<br \/>\n            if(error) {<br \/>\n                response = {<br \/>\n                    body: JSON.stringify({<br \/>\n                        code: error.code,<br \/>\n                        message: error.message<br \/>\n                    })<br \/>\n                };<br \/>\n                reject(response);<br \/>\n            }<br \/>\n            data.id = id;<br \/>\n            response = {<br \/>\n                body: JSON.stringify(validation.value)<br \/>\n            };<br \/>\n            resolve(response);<br \/>\n        });<br \/>\n    });<br \/>\n}<\/p>\n<p>exports.main = main;[\/crayon]<\/p>\n\n\n\n<p>The above code is a bit much, so we need to figure out what is going on. Let&#8217;s start with the variable that exists outside of our function:<\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]var bucket = null;[\/crayon]<\/p>\n\n\n\n<p>It isn&#8217;t the best idea to establish a new connection every time the function is called. Instead, we can keep a global instance of the open Couchbase Bucket and for as long as it exists, use it. Just note that it won&#8217;t always exist because OpenWhisk will destroy functions after a while of inactivity.<\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]if(bucket == null) {<br \/>\n    var cluster = new Couchbase.Cluster(&#8220;couchbase:\/\/&#8221; + params.host);<br \/>\n    cluster.authenticate(params.username, params.password);<br \/>\n    bucket = cluster.openBucket(params.bucketName);<br \/>\n}[\/crayon]<\/p>\n\n\n\n<p>Inside our function we check to see if the Bucket is already open. If the Bucket is not open, we establish a connection using parameters passed into the function. When the time comes, we&#8217;ll be defining default parameters which contain this connection information.<\/p>\n\n\n\n<p>Since we&#8217;re creating data, we need to validate that the input is correct.<\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]var schema = Joi.object().keys({<br \/>\n    firstname: Joi.string().required(),<br \/>\n    lastname: Joi.string().required(),<br \/>\n    type: Joi.string().forbidden().default(&#8220;person&#8221;)<br \/>\n});[\/crayon]<\/p>\n\n\n\n<p>We&#8217;re expecting a <code>firstname<\/code> and <code>lastname<\/code> value to be present. We&#8217;re also expecting a <code>type<\/code> to not be present. We can validate this with the following:<\/p>\n\n\n<p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]var validation = Joi.validate(data, schema, { stripUnknown: true });<br \/>\nif(validation.error) {<br \/>\n    response = {<br \/>\n        statusCode: 500,<br \/>\n        body: JSON.stringify(validation.error.details)<br \/>\n    };<br \/>\n    reject(response);<br \/>\n}[\/crayon]<\/p>\n\n\n\n<p>The <code>stripUnknown<\/code> option will remove data not defined in the scheme. We need to strip data because our input and connection information will exist in the same payload. We don&#8217;t want the connection information to be saved in our documents. If there is a validation error, it will be returned. If there was no validation error, we can proceed to inserting the data.<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]create<br \/>\n&#8212; package.json<br \/>\n&#8212; create.js<br \/>\nretrieve<br \/>\n&#8212; package.json<br \/>\n&#8212; retrieve.js<br \/>\nupdate<br \/>\n&#8212; package.json<br \/>\n&#8212; update.js<br \/>\ndelete<br \/>\n&#8212; package.json<br \/>\n&#8212; delete.js[\/crayon]<\/p>\n0<\/p>\n\n\n\n<p>We can generate a new unique key and save the validated data as a document. The data itself will be returned as a response.<\/p>\n\n\n\n<p>The other functions will follow this same strategy, more or less.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Designing a Function for Retrieving Data with N1QL<\/h2>\n\n\n\n<p>Now that we have data, let&#8217;s try to retrieve it from the database with a function invocation. Navigate to your\u00a0<strong>retrieve<\/strong> directory and execute the following from the command line:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]create<br \/>\n&#8212; package.json<br \/>\n&#8212; create.js<br \/>\nretrieve<br \/>\n&#8212; package.json<br \/>\n&#8212; retrieve.js<br \/>\nupdate<br \/>\n&#8212; package.json<br \/>\n&#8212; update.js<br \/>\ndelete<br \/>\n&#8212; package.json<br \/>\n&#8212; delete.js[\/crayon]<\/p>\n1<\/p>\n\n\n\n<p>Because we won&#8217;t be creating data, we don&#8217;t need to generate unique values or validate any user data. For this reason, we only need the Couchbase SDK for this function.<\/p>\n\n\n\n<p>Open the project&#8217;s\u00a0<strong>retrieve\/retrieve.js<\/strong> file and include the following:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]create<br \/>\n&#8212; package.json<br \/>\n&#8212; create.js<br \/>\nretrieve<br \/>\n&#8212; package.json<br \/>\n&#8212; retrieve.js<br \/>\nupdate<br \/>\n&#8212; package.json<br \/>\n&#8212; update.js<br \/>\ndelete<br \/>\n&#8212; package.json<br \/>\n&#8212; delete.js[\/crayon]<\/p>\n2<\/p>\n\n\n\n<p>Let&#8217;s skip over what we&#8217;ve already seen in the previous function and jump to what&#8217;s new. Once we&#8217;re connected to an open Bucket, we can create a N1QL query.<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]create<br \/>\n&#8212; package.json<br \/>\n&#8212; create.js<br \/>\nretrieve<br \/>\n&#8212; package.json<br \/>\n&#8212; retrieve.js<br \/>\nupdate<br \/>\n&#8212; package.json<br \/>\n&#8212; update.js<br \/>\ndelete<br \/>\n&#8212; package.json<br \/>\n&#8212; delete.js[\/crayon]<\/p>\n3<\/p>\n\n\n\n<p>This N1QL query is SQL-like and it will allow us to retrieve all documents that match certain criteria. If there are any errors, return them as a response, otherwise return the result set.<\/p>\n\n\n\n<p>Because we&#8217;re not validating anything, this function for retrieving data was much simpler.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Designing a Function for Updating Data with Subdocument Mutations<\/h2>\n\n\n\n<p>Now let&#8217;s say that we want to update documents within the database. Instead of retrieving documents, making changes, then saving those changes, we&#8217;re going to submit changes directly to the database and let the database figure things out.<\/p>\n\n\n\n<p>Navigate into the project&#8217;s\u00a0<strong>update<\/strong> directory and execute the following from the command line:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]create<br \/>\n&#8212; package.json<br \/>\n&#8212; create.js<br \/>\nretrieve<br \/>\n&#8212; package.json<br \/>\n&#8212; retrieve.js<br \/>\nupdate<br \/>\n&#8212; package.json<br \/>\n&#8212; update.js<br \/>\ndelete<br \/>\n&#8212; package.json<br \/>\n&#8212; delete.js[\/crayon]<\/p>\n4<\/p>\n\n\n\n<p>Because we&#8217;re accepting user data, we want to validate that data. We&#8217;re not creating data so we don&#8217;t need to generate any unique keys.<\/p>\n\n\n\n<p>Open the project&#8217;s\u00a0<strong>update\/update.js<\/strong> file and include the following:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]create<br \/>\n&#8212; package.json<br \/>\n&#8212; create.js<br \/>\nretrieve<br \/>\n&#8212; package.json<br \/>\n&#8212; retrieve.js<br \/>\nupdate<br \/>\n&#8212; package.json<br \/>\n&#8212; update.js<br \/>\ndelete<br \/>\n&#8212; package.json<br \/>\n&#8212; delete.js[\/crayon]<\/p>\n5<\/p>\n\n\n\n<p>Does the above code look familiar? It should, because we&#8217;re following the same strategy.<\/p>\n\n\n\n<p>Our validation logic is slightly different in this example:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]create<br \/>\n&#8212; package.json<br \/>\n&#8212; create.js<br \/>\nretrieve<br \/>\n&#8212; package.json<br \/>\n&#8212; retrieve.js<br \/>\nupdate<br \/>\n&#8212; package.json<br \/>\n&#8212; update.js<br \/>\ndelete<br \/>\n&#8212; package.json<br \/>\n&#8212; delete.js[\/crayon]<\/p>\n6<\/p>\n\n\n\n<p>We want to edit a particular document so a key is required. We don&#8217;t know what the user wants to update so we set the properties as optional.<\/p>\n\n\n\n<p>To do updates we&#8217;re going to be doing subdocument operations on our documents. To do this, we can use a mutation builder.<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]create<br \/>\n&#8212; package.json<br \/>\n&#8212; create.js<br \/>\nretrieve<br \/>\n&#8212; package.json<br \/>\n&#8212; retrieve.js<br \/>\nupdate<br \/>\n&#8212; package.json<br \/>\n&#8212; update.js<br \/>\ndelete<br \/>\n&#8212; package.json<br \/>\n&#8212; delete.js[\/crayon]<\/p>\n7<\/p>\n\n\n\n<p>We provide a document to alter and whatever paths the properties exist at. The paths could be much more complex than the examples used here.<\/p>\n\n\n\n<p>With the set of mutations defined, we can execute them against the database.<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]create<br \/>\n&#8212; package.json<br \/>\n&#8212; create.js<br \/>\nretrieve<br \/>\n&#8212; package.json<br \/>\n&#8212; retrieve.js<br \/>\nupdate<br \/>\n&#8212; package.json<br \/>\n&#8212; update.js<br \/>\ndelete<br \/>\n&#8212; package.json<br \/>\n&#8212; delete.js[\/crayon]<\/p>\n8<\/p>\n\n\n\n<p>Depending on the result, a response will be returned from the invocation of the function.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Designing a Function for Removing Data<\/h2>\n\n\n\n<p>We&#8217;re at our final function in a package of CRUD operations. The time has come to delete data from the database.<\/p>\n\n\n\n<p>Navigate to the\u00a0<strong>delete<\/strong> directory and execute the following command:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]create<br \/>\n&#8212; package.json<br \/>\n&#8212; create.js<br \/>\nretrieve<br \/>\n&#8212; package.json<br \/>\n&#8212; retrieve.js<br \/>\nupdate<br \/>\n&#8212; package.json<br \/>\n&#8212; update.js<br \/>\ndelete<br \/>\n&#8212; package.json<br \/>\n&#8212; delete.js[\/crayon]<\/p>\n9<\/p>\n\n\n\n<p>We&#8217;ll be accepting document keys to be deleted so we&#8217;ll need to validate the input. Likewise we also need the Couchbase SDK to work with the database.<\/p>\n\n\n\n<p>Open the project&#8217;s\u00a0<strong>delete\/delete.js<\/strong> file and include the following JavaScript code:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]npm init -y[\/crayon]<\/p>\n0<\/p>\n\n\n\n<p>You&#8217;re probably seeing the bigger picture now in regards to function creation with OpenWhisk and Couchbase, so we&#8217;re not going to walk through the above function for deleting documents.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Packaging and Deploying the Functions to OpenWhisk with Docker<\/h2>\n\n\n\n<p>We have a set of functions ready to go, but we can&#8217;t just package and deploy them to Bluemix. If we did that, we&#8217;d get a bunch of errors. Bluemix uses a special flavor of Linux with a certain architecture. I downloaded the dependencies on my Mac which isn&#8217;t a match.<\/p>\n\n\n\n<p>Remember that article I wrote a while back titled,\u00a0<a href=\"https:\/\/www.thepolyglotdeveloper.com\/2017\/12\/deploying-native-nodejs-dependencies-aws-lambda\/\" target=\"_blank\" rel=\"noopener\">Deploying Native Node.js Dependencies On AWS Lambda<\/a>? We need to do something similar for OpenWhisk with Docker.<\/p>\n\n\n\n<p>With Docker installed and ready to go, execute the following from the CLI:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]npm init -y[\/crayon]<\/p>\n1<\/p>\n\n\n\n<p>The above commands will download an appropriate OpenWhisk Docker image for Node.js. Then we deploy a container with that image in interactive terminal mode. This container will also have a mapped volume. I am mapping my local project directory to a directory within the container.<\/p>\n\n\n\n<p>After the command has executed and the container is deployed, you should be in the shell within the container.<\/p>\n\n\n\n<p>For each function, execute the following:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]npm init -y[\/crayon]<\/p>\n2<\/p>\n\n\n\n<p>Remember, installing dependencies from our host machine isn&#8217;t good enough. We need to compile the dependencies for Bluemix. Docker will compile these dependencies and since the directory is mapped, we can use them from the host machine.<\/p>\n\n\n\n<p>After each functions packages are installed, we can bundle them and deploy them.<\/p>\n\n\n\n<p>From the host machine, create a ZIP archive of each of the functions. The archive should contain the\u00a0<strong>package.json<\/strong> file, the JavaScript file, and the\u00a0<strong>node_modules<\/strong> directory.<\/p>\n\n\n\n<p>If you&#8217;re on a Mac or computer with a ZIP CLI, execute the following:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]npm init -y[\/crayon]<\/p>\n3<\/p>\n\n\n\n<p>When you have a ZIP of each function, they can be deployed by executing the following:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]npm init -y[\/crayon]<\/p>\n4<\/p>\n\n\n\n<p>I introduced some new things in the above command.<\/p>\n\n\n\n<p>First, we&#8217;re creating a package called\u00a0<strong>couchbase<\/strong> and in this package we have a\u00a0<strong>delete<\/strong> function that is based off the\u00a0<strong>delete.zip<\/strong> file. I&#8217;m also passing some default parameters. These parameters will be our connection information. Since this information is sensitive, we are not passing them when invoking the function, rather creating the function.<\/p>\n\n\n\n<p>Execute a variation of the above command for each of your functions.<\/p>\n\n\n\n<p>To execute your function, try running something like the following:<\/p>\n\n\n\n<p><p>[crayon lang=&#8221;default&#8221; decode=&#8221;true&#8221;]npm init -y[\/crayon]<\/p>\n5<\/p>\n\n\n\n<p>The above command should pass in a few parameters to pass validation. The function is invoked in a blocking manner, and if it succeeds, our data will be saved in the database and returned in the response.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>You just saw how to create a package of serverless functions for OpenWhisk that communicate with the NoSQL database, <a href=\"https:\/\/www.couchbase.com\" target=\"_blank\" rel=\"noopener\">Couchbase<\/a>. OpenWhisk can be used as an alternative to AWS Lambda, but the two are certainly not the only options available. Regardless on what you choose, functions as a service (FaaS) are very scalable solutions for massive applications.<\/p>\n\n\n\n<p>Want to see another OpenWhisk example? Check out a tutorial I wrote titled,\u00a0<a href=\"https:\/\/www.thepolyglotdeveloper.com\/2017\/12\/convert-nodejs-restful-api-serverless-openwhisk\/\">Convert a Node.js RESTful API to Serverless with OpenWhisk<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>If you&#8217;ve been keeping up with my content, you&#8217;ll remember that I had written an article titled,\u00a0Use AWS Lambda and API Gateway with Node.js and Couchbase NoSQL. In this article we had explored using Amazon&#8217;s Serverless services to create Lambda functions that interact with Couchbase, our NoSQL database. However, Lambda isn&#8217;t the only serverless, otherwise [&hellip;]<\/p>\n","protected":false},"author":63,"featured_media":18,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[127,54,49],"tags":[294,295,296,6,297],"ppma_author":[148],"class_list":["post-1230","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-application-design","category-couchbase-server","category-node-js","tag-actions","tag-faas","tag-functions","tag-nosql-database","tag-serverless"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.6 (Yoast SEO v27.6) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Use OpenWhisk for FaaS with Node.js and Couchbase NoSQL<\/title>\n<meta name=\"description\" content=\"Learn how to create and deploy highly scalable applications as functions using OpenWhisk on Bluemix for FaaS and Couchbase for NoSQL JSON data.\" \/>\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\/es\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/\" \/>\n<meta property=\"og:locale\" content=\"es_MX\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Use OpenWhisk for FaaS with Node.js and Couchbase NoSQL\" \/>\n<meta property=\"og:description\" content=\"Learn how to create and deploy highly scalable applications as functions using OpenWhisk on Bluemix for FaaS and Couchbase for NoSQL JSON data.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/es\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/\" \/>\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=\"2018-02-08T15:00:16+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/couchbase-nosql-dbaas.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1800\" \/>\n\t<meta property=\"og:image:height\" content=\"630\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\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=\"12 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/\"},\"author\":{\"name\":\"Nic Raboy, Developer Advocate, Couchbase\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/person\\\/bb545ebe83bb2d12f91095811d0a72e1\"},\"headline\":\"Use OpenWhisk for FaaS with Node.js and Couchbase NoSQL\",\"datePublished\":\"2018-02-08T15:00:16+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/\"},\"wordCount\":2561,\"commentCount\":3,\"publisher\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/05\\\/couchbase-nosql-dbaas.png\",\"keywords\":[\"actions\",\"faas\",\"functions\",\"NoSQL Database\",\"serverless\"],\"articleSection\":[\"Application Design\",\"Couchbase Server\",\"Node.js\"],\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/\",\"name\":\"Use OpenWhisk for FaaS with Node.js and Couchbase NoSQL\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/05\\\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2018-02-08T15:00:16+00:00\",\"description\":\"Learn how to create and deploy highly scalable applications as functions using OpenWhisk on Bluemix for FaaS and Couchbase for NoSQL JSON data.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/#breadcrumb\"},\"inLanguage\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/#primaryimage\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/05\\\/couchbase-nosql-dbaas.png\",\"contentUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/05\\\/couchbase-nosql-dbaas.png\",\"width\":1800,\"height\":630},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Use OpenWhisk for FaaS with Node.js and Couchbase NoSQL\"}]},{\"@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\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/logo\\\/image\\\/\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/06\\\/logo.svg\",\"contentUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/5\\\/2026\\\/06\\\/logo.svg\",\"width\":\"1024\",\"height\":\"1024\",\"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\":\"es\",\"@id\":\"https:\\\/\\\/secure.gravatar.com\\\/avatar\\\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g8863514d8bed0cf6080f23db40e00354\",\"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\\\/es\\\/author\\\/nic-raboy-2\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Use OpenWhisk for FaaS with Node.js and Couchbase NoSQL","description":"Learn how to create and deploy highly scalable applications as functions using OpenWhisk on Bluemix for FaaS and Couchbase for NoSQL JSON data.","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\/es\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/","og_locale":"es_MX","og_type":"article","og_title":"Use OpenWhisk for FaaS with Node.js and Couchbase NoSQL","og_description":"Learn how to create and deploy highly scalable applications as functions using OpenWhisk on Bluemix for FaaS and Couchbase for NoSQL JSON data.","og_url":"https:\/\/www.couchbase.com\/blog\/es\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/","og_site_name":"The Couchbase Blog","article_author":"https:\/\/www.facebook.com\/thepolyglotdeveloper","article_published_time":"2018-02-08T15:00:16+00:00","og_image":[{"width":1800,"height":630,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/couchbase-nosql-dbaas.png","type":"image\/png"}],"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":"12 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/"},"author":{"name":"Nic Raboy, Developer Advocate, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1"},"headline":"Use OpenWhisk for FaaS with Node.js and Couchbase NoSQL","datePublished":"2018-02-08T15:00:16+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/"},"wordCount":2561,"commentCount":3,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/couchbase-nosql-dbaas.png","keywords":["actions","faas","functions","NoSQL Database","serverless"],"articleSection":["Application Design","Couchbase Server","Node.js"],"inLanguage":"es","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/","url":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/","name":"Use OpenWhisk for FaaS with Node.js and Couchbase NoSQL","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/couchbase-nosql-dbaas.png","datePublished":"2018-02-08T15:00:16+00:00","description":"Learn how to create and deploy highly scalable applications as functions using OpenWhisk on Bluemix for FaaS and Couchbase for NoSQL JSON data.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/"]}]},{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/couchbase-nosql-dbaas.png","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/05\/couchbase-nosql-dbaas.png","width":1800,"height":630},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/use-openwhisk-for-faas-with-node-js-and-couchbase-nosql\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Use OpenWhisk for FaaS with Node.js and Couchbase NoSQL"}]},{"@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":"es"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"The Couchbase Blog","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/06\/logo.svg","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/5\/2026\/06\/logo.svg","width":"1024","height":"1024","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":"es","@id":"https:\/\/secure.gravatar.com\/avatar\/bedeb68368d4681aca4c74fe5f697f0c423b80d498ec50fd915ba018b72c101f?s=96&d=mm&r=g8863514d8bed0cf6080f23db40e00354","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\/es\/author\/nic-raboy-2\/"}]}},"acf":[],"authors":[{"term_id":148,"user_id":63,"is_guest":0,"slug":"nic-raboy-2","display_name":"Nic Raboy, Developer Advocate, Couchbase","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/?s=96&d=mm&r=g","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/1230","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/users\/63"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/comments?post=1230"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/1230\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media\/18"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media?parent=1230"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/categories?post=1230"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/tags?post=1230"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/ppma_author?post=1230"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}