{"id":3841,"date":"2017-07-20T07:00:11","date_gmt":"2017-07-20T14:00:11","guid":{"rendered":"http:\/\/www.couchbase.com\/blog\/?p=3841"},"modified":"2025-06-13T18:46:03","modified_gmt":"2025-06-14T01:46:03","slug":"couchbase-meetup-project-ocean-stack-available","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/pt\/couchbase-meetup-project-ocean-stack-available\/","title":{"rendered":"Projeto Meetup do Couchbase na pilha OCEAN dispon\u00edvel"},"content":{"rendered":"<p>I was recently at two different Meetup groups in Southern California presenting on what I&#8217;m calling, the OCEAN stack, which is composed of Ottoman.js, <a href=\"https:\/\/www.couchbase.com\" target=\"_blank\" rel=\"noopener noreferrer\">Couchbase Server<\/a>, Express Framework, Angular, and Node.js.<\/p>\n<p>We had a great turnout of developers hungry to learn about a JavaScript with NoSQL stack.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-3854 size-full\" src=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/2017\/07\/oc-angular-meetup.jpg\" alt=\"OC Angular Meetup\" width=\"1200\" height=\"549\" srcset=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/07\/oc-angular-meetup.jpg 1200w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/07\/oc-angular-meetup-300x137.jpg 300w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/07\/oc-angular-meetup-1024x468.jpg 1024w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/07\/oc-angular-meetup-768x351.jpg 768w, https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/07\/oc-angular-meetup-20x9.jpg 20w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/p>\n<p>As per my promise to the groups, I have done a tutorial write-up of the content that I shared so that way everyone can benefit. We&#8217;re going to see how to develop a functional web application using various JavaScript technologies and NoSQL.<\/p>\n<p><!--more--><\/p>\n<p>Before jumping into the code, the assumption is that you&#8217;ve got Couchbase Server installed and configured as well as Node.js. The focus here will be development, not environment configurations.<\/p>\n<p>We&#8217;re going to build an API using Ottoman.js and then with N1QL instead of Ottoman.js. Both APIs will be consumed by a front-end developed in Angular.<\/p>\n<h2>Developing an API with Ottoman.js, Express Framework, and Couchbase NoSQL<\/h2>\n<p>The first step in any Node.js project is to configure a project and download all the dependencies. Creating a fresh project and downloading the dependencies can be handled with the following commands:<\/p>\n<pre class=\"lang:default decode:true \">npm init --y\r\nnpm install ottoman couchbase express body-parser cors --save<\/pre>\n<p>The first command will create the project&#8217;s\u00a0<strong>package.json<\/strong> and the second command will get our dependencies which include the\u00a0<code>body-parser<\/code> for accepting JSON bodies in the request and the\u00a0<code>cors<\/code> package for cross-origin related issues that might come up when using Angular on a different domain or port.<\/p>\n<p>To bootstrap our application, we&#8217;re going to create a file called\u00a0<strong>app.js<\/strong> that contains the following JavaScript code:<\/p>\n<pre class=\"lang:default decode:true \">var Couchbase = require(\"couchbase\");\r\nvar Express = require(\"express\");\r\nvar Cors = require(\"cors\");\r\nvar BodyParser = require(\"body-parser\");\r\nvar Ottoman = require(\"ottoman\");\r\n\r\nvar app = Express();\r\n\r\napp.use(BodyParser.json());\r\napp.use(BodyParser.urlencoded({ extended: true }));\r\napp.use(Cors());\r\n\r\nvar cluster = new Couchbase.Cluster(\"couchbase:\/\/localhost\");\r\nvar bucket = cluster.openBucket(\"default\", \"\");\r\nOttoman.store = new Ottoman.CbStoreAdapter(bucket, Couchbase);\r\n\r\napp.get(\"\/people\", function(request, response) { });\r\napp.get(\"\/person\/:id\", function(request, response) { });\r\napp.post(\"\/person\/:id?\", function(request, response) { });\r\napp.delete(\"\/person\/:id\", function(request, response) { });\r\n\r\nvar server = app.listen(3000, function() {\r\n    console.log(\"Listening on port \" + server.address().port + \"...\");\r\n});<\/pre>\n<p>Essentially we&#8217;ve just imported our downloaded dependencies, defined our API endpoints, established a connect to Couchbase Server, and started serving the application.<\/p>\n<p>The API endpoints will be for basic CRUD operations against our database. Because we&#8217;re using Ottoman.js which is an object document modeler (ODM), we need to define our model. Above the endpoint definitions, add the following:<\/p>\n<pre class=\"lang:default decode:true \">var Person = Ottoman.model(\"Person\", {\r\n    firstname: \"string\",\r\n    lastname: \"string\",\r\n    social_media: {\r\n        website: \"string\",\r\n        twitter: \"string\"\r\n    }\r\n});<\/pre>\n<p>This model can be infinitely complex, but ours will just represent a simple user profile. This is the model we&#8217;ll manipulate in our code as well as the database.<\/p>\n<p>Because our database is currently empty, it makes sense to work on our endpoint for creating data. Edit the following chunk of JavaScript within your project:<\/p>\n<pre class=\"lang:default decode:true \">app.post(\"\/person\/:id?\", function(request, response) {\r\n    if(!request.body) {\r\n        return response.status(401).send({ \"message\": \"A POST body is required!\" });\r\n    }\r\n    Person.getById(request.params.id, function(error, result) {\r\n        if(error) {\r\n            result = new Person(request.body);\r\n        } else {\r\n            Object.assign(result, request.body);\r\n        }\r\n        result.save(function(error) {\r\n            if(error) {\r\n                return response.status(500).send(error);\r\n            }\r\n            response.send(request.body);\r\n        });\r\n    });\r\n});<\/pre>\n<p>The above endpoint is probably the most complex part of our project. We are saying that we are requiring a POST body. It doesn&#8217;t matter what it is, as long as it exists.<\/p>\n<p>We hit two birds with one stone in this endpoint. We are accomplishing a create as well as an update. First we do a lookup by the passed id. It doesn&#8217;t matter if an id was passed or not. If there was an error for reasons of maybe an id not existing, a new ODM model will be created based on our definition, otherwise it will be merged with what we got from our lookup based on id.<\/p>\n<p>Once we have our model complete, we can save and return the results to the user.<\/p>\n<p>The next logical step is to complete the endpoint for finding a particular person by their id value. Add the following JavaScript to your project:<\/p>\n<pre class=\"lang:default decode:true \">app.get(\"\/person\/:id\", function(request, response) {\r\n    if(!request.params.id) {\r\n        return response.status(401).send({ \"message\": \"An `id` is required!\" });\r\n    }\r\n    Person.getById(request.params.id, function(error, result) {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        response.send(result);\r\n    });\r\n});<\/pre>\n<p>Using a passed id value we can get someone from the database and return it back to the client that had issued the request.<\/p>\n<p>There might be times where we want to get all records that match the model. There may even be times where we wish to query based on properties other than the id value. In this case we can use a\u00a0<code>find<\/code> command:<\/p>\n<pre class=\"lang:default decode:true \">app.get(\"\/people\", function(request, response) {\r\n    Person.find({}, { consistency: Ottoman.Consistency.LOCAL }, function(error, result) {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        response.send(result);\r\n    });\r\n});<\/pre>\n<p>By providing an empty object in our command, we are requesting all documents that match the model. This could have easily been extended to particular matching properties. We also want to make sure that our index is updated before we complete the query. By default, Couchbase is performance focused which means if your index hasn&#8217;t updated yet, your results might be missing some data. Depending on the circumstance, you can change the scan consistency like we have in the above.<\/p>\n<p>Finally, we can build our endpoint for removing documents from the database:<\/p>\n<pre class=\"lang:default decode:true \">app.delete(\"\/person\/:id\", function(request, response) {\r\n    if(!request.params.id) {\r\n        return response.status(401).send({ \"message\": \"An `id` is required!\" });\r\n    }\r\n    Person.getById(request.params.id, function(error, result) {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        result.remove(function(error) {\r\n            response.send({ \"message\": \"Deleted `\" + request.params.id  + \"`\"});\r\n        });\r\n    });\r\n});<\/pre>\n<p>We first obtain a document by the passed id, and if it exists, we can issue a\u00a0<code>remove<\/code> on it to delete it.<\/p>\n<p>If you run this application and use a tool like <a href=\"https:\/\/www.thepolyglotdeveloper.com\/2015\/01\/using-postman-troubleshoot-restful-api-requests\/\" target=\"_blank\" rel=\"noopener noreferrer\">Postman<\/a>, you can test your basic, but fully functional API.<\/p>\n<h2>Developing an API with Express Framework, N1QL, and Couchbase NoSQL<\/h2>\n<p>Let&#8217;s say that using an ODM like Ottoman.js is not your jam and you&#8217;d prefer to fabricate your own queries. Removing Ottoman.js gives us the CEAN stack and it is made possible with N1QL.<\/p>\n<p>While our project structure will be quite similar, we&#8217;re going to create a new project for our own sanity.<\/p>\n<pre class=\"lang:default decode:true \">npm init --y\r\nnpm install couchbase express body-parser cors uuid --save<\/pre>\n<p>We have similar dependencies as seen in the previous project, but this time around we are obtaining a package for generating UUID values. Ottoman.js created id values for us, but with N1QL, it is up to the developer. One of many ways to create an id is via a UUID.<\/p>\n<p>Within a new\u00a0<strong>app.js<\/strong> file, you might have the following boilerplate code:<\/p>\n<pre class=\"lang:default decode:true \">var Express = require(\"express\");\r\nvar Couchbase = require(\"couchbase\");\r\nvar BodyParser = require(\"body-parser\");\r\nvar UUID = require(\"uuid\");\r\nvar Cors = require(\"cors\");\r\n\r\nvar app = Express();\r\nvar N1qlQuery = Couchbase.N1qlQuery;\r\n\r\napp.use(BodyParser.json());\r\napp.use(BodyParser.urlencoded({ extended: true }));\r\napp.use(Cors());\r\n\r\nvar cluster = new Couchbase.Cluster(\"couchbase:\/\/localhost\");\r\nvar bucket = cluster.openBucket(\"default\", \"\");\r\n\r\napp.get(\"\/people\", function(request, response) { });\r\napp.get(\"\/person\/:id?\", function(request, response) { });\r\napp.post(\"\/person\/:id?\", function(request, response) { });\r\napp.delete(\"\/person\/:id?\", function(request, response) { });\r\n\r\nvar server = app.listen(3000, function() {\r\n    console.log(\"Listening on port \" + server.address().port + \"...\");\r\n});<\/pre>\n<p>That should look pretty familiar, but this time we swapped out Ottoman.js with N1QL preparation.<\/p>\n<p>Just like with the previous example, we&#8217;re going to worry about saving data before we try to read from the database. Check out the following endpoint code:<\/p>\n<pre class=\"lang:default decode:true \">app.post(\"\/person\/:id?\", function(request, response) {\r\n    if(!request.body) {\r\n        return response.status(401).send({ \"message\": \"A POST body is required!\" });\r\n    }\r\n    var id = request.params.id ? request.params.id : UUID.v4();\r\n    request.body._id = id;\r\n    var statement = N1qlQuery.fromString(\"UPSERT INTO `\" + bucket._name + \"` (KEY, VALUE) VALUES ($id, $data) RETURNING `\" + bucket._name + \"`.*\");\r\n    bucket.query(statement, { \"id\": id, \"data\": request.body }, function(error, result) {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        response.send(result);\r\n    });\r\n});<\/pre>\n<p>If we receive an id with our request, we&#8217;re going to use it and assume we are doing an update, otherwise we&#8217;re going to generate a new id and do a create. Both operations can be accomplished with a single\u00a0<code>UPSERT<\/code> query. To prevent SQL injection attacks, we&#8217;re going to use a parameterized query to parameterize our user defined data.<\/p>\n<p>If we know the id and we wish to find a particular document, we can use a\u00a0<code>SELECT<\/code> query for the job:<\/p>\n<pre class=\"lang:default decode:true \">app.get(\"\/person\/:id?\", function(request, response) {\r\n    if(!request.params.id) {\r\n        return response.status(401).send({ \"message\": \"An `id` is required!\" });\r\n    }\r\n    var statement = N1qlQuery.fromString(\"SELECT `\" + bucket._name + \"`.* FROM `\" + bucket._name + \"` WHERE META().id = $id\");\r\n    bucket.query(statement, { \"id\": request.params.id }, function(error, result) {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        response.send(result);\r\n    });\r\n});<\/pre>\n<p>In both queries we are returning either the error or the results back to the client that issued the request.<\/p>\n<p>If we wish to query for all our documents, we can drop the\u00a0<code>WHERE<\/code> condition and define our scan consistency like so:<\/p>\n<pre class=\"lang:default decode:true \">app.get(\"\/people\", function(request, response) {\r\n    var statement = N1qlQuery.fromString(\"SELECT META().id, `\" + bucket._name + \"`.* FROM `\" + bucket._name + \"`\").consistency(N1qlQuery.Consistency.REQUEST_PLUS);\r\n    bucket.query(statement, function(error, result) {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        response.send(result);\r\n    });\r\n});<\/pre>\n<p>Being that N1QL is just another flavor of SQL, our query possibilities are quite large. This API example doesn&#8217;t even cover a fraction of what is possible.<\/p>\n<p>Finally if we wanted to remove a document by its id value, we could do something like the following:<\/p>\n<pre class=\"lang:default decode:true \">app.delete(\"\/person\/:id?\", function(request, response) {\r\n    if(!request.params.id) {\r\n        return response.status(401).send({ \"message\": \"An `id` is required!\" });\r\n    }\r\n    var statement = N1qlQuery.fromString(\"DELETE FROM `\" + bucket._name + \"` WHERE META().id = $id\");\r\n    bucket.query(statement, { \"id\": request.params.id }, function(error, result) {\r\n        if(error) {\r\n            return response.status(500).send(error);\r\n        }\r\n        response.send(result);\r\n    });\r\n});<\/pre>\n<p>None of these N1QL based endpoints were difficult. There is no wrong choice when it comes to Ottoman.js or N1QL, it really just comes down to preference.<\/p>\n<h2>Building a Client Frontend with Angular and TypeScript<\/h2>\n<p>With the backend API out of the way, we can focus on a very simple, but functional client frontend. This frontend will work with both our OCEAN stack and our CEAN stack example. It is always nice to have something modular.<\/p>\n<p>Assuming you have the <a href=\"https:\/\/cli.angular.io\/\" target=\"_blank\" rel=\"noopener noreferrer\">Angular CLI<\/a> installed and configured, we need to have a fresh project:<\/p>\n<pre class=\"lang:default decode:true \">ng new couchbase-project<\/pre>\n<p>The above command will create a new project with all our required Angular dependencies.<\/p>\n<p>At this point you can either use the CLI to generate each of our pages, or do them manually. Both will put you in the same place.<\/p>\n<p>Create the following files and directories in your project however you want:<\/p>\n<pre class=\"lang:default decode:true \">mkdir -p src\/app\/list\r\nmkdir -p src\/app\/alter\r\ntouch src\/app\/list\/list.component.ts\r\ntouch src\/app\/list\/list.component.html\r\ntouch src\/app\/alter\/alter.component.ts\r\ntouch src\/app\/alter\/alter.component.html<\/pre>\n<p>Starting with what will be our\u00a0<code>AlterComponent<\/code>, open the project&#8217;s\u00a0<strong>src\/app\/alter\/alter.component.ts<\/strong> file and include the following TypeScript code:<\/p>\n<pre class=\"lang:default decode:true \">import { Component, OnInit } from \"@angular\/core\";\r\nimport { Http, Headers, RequestOptions } from \"@angular\/http\";\r\nimport { Location } from \"@angular\/common\";\r\nimport { ActivatedRoute } from \"@angular\/router\";\r\nimport \"rxjs\/Rx\";\r\n\r\n@Component({\r\n    moduleId: module.id,\r\n    templateUrl: \"alter.component.html\"\r\n})\r\nexport class AlterComponent implements OnInit {\r\n\r\n    public id: string;\r\n    public input: any;\r\n\r\n    public constructor(private http: Http, private location: Location, private route: ActivatedRoute) {\r\n        this.id = \"\";\r\n        this.input = {\r\n            \"firstname\": \"\",\r\n            \"lastname\": \"\",\r\n            \"social_media\": {\r\n                \"website\": \"\",\r\n                \"twitter\": \"\"\r\n            }\r\n        };\r\n    }\r\n\r\n    public ngOnInit() {\r\n        this.route.params.subscribe(params =&gt; {\r\n            this.id = params[\"id\"];\r\n            if(this.id) {\r\n                this.http.get(\"https:\/\/localhost:3000\/person\/\" + this.id)\r\n                    .map(result =&gt; result.json())\r\n                    .subscribe(result =&gt; {\r\n                        this.input = result instanceof Array ? result[0] : result;\r\n                    });\r\n            }\r\n        });\r\n    }\r\n\r\n    public save() {\r\n        let headers = new Headers({ \"Content-Type\": \"application\/json\" });\r\n        let options = new RequestOptions({ headers: headers });\r\n        let url = \"https:\/\/localhost:3000\/person\/\";\r\n        if(this.id) {\r\n            url += this.id;\r\n        }\r\n        if(this.input.firstname &amp;&amp; this.input.lastname) {\r\n            this.http.post(url, JSON.stringify(this.input), options)\r\n                .map(result =&gt; result.json())\r\n                .subscribe(result =&gt; {\r\n                    this.location.back();\r\n                });\r\n        }\r\n    }\r\n\r\n}<\/pre>\n<p>There is a lot happening in the above code, so let&#8217;s break it down.<\/p>\n<p>Inside the\u00a0<code>constructor<\/code> method we have the following:<\/p>\n<pre class=\"lang:default decode:true \">public constructor(private http: Http, private location: Location, private route: ActivatedRoute) {\r\n    this.id = \"\";\r\n    this.input = {\r\n        \"firstname\": \"\",\r\n        \"lastname\": \"\",\r\n        \"social_media\": {\r\n            \"website\": \"\",\r\n            \"twitter\": \"\"\r\n        }\r\n    };\r\n}<\/pre>\n<p>We are initializing our public variables, one that represents our form data and the other that represents a possible id for an existing document. In addition to this we are also injecting several Angular services for use in the component.<\/p>\n<p>Again, we are going to hit two birds with one stone. This component will represent a screen for creating and updating documents.<\/p>\n<p>Within the\u00a0<code>ngOnInit<\/code> method we take a potential document id and do a lookup for the data that resides at it:<\/p>\n<pre class=\"lang:default decode:true \">public ngOnInit() {\r\n    this.route.params.subscribe(params =&gt; {\r\n        this.id = params[\"id\"];\r\n        if(this.id) {\r\n            this.http.get(\"https:\/\/localhost:3000\/person\/\" + this.id)\r\n                .map(result =&gt; result.json())\r\n                .subscribe(result =&gt; {\r\n                    this.input = result instanceof Array ? result[0] : result;\r\n                });\r\n        }\r\n    });\r\n}<\/pre>\n<p>If no id is present, nothing will happen and our\u00a0<code>input<\/code> variable won&#8217;t be populated with anything beyond empty strings. The lookup will be an HTTP request against the API endpoint that we had previously created either with Ottoman.js or N1QL.<\/p>\n<p>The last thing to do for this component is save:<\/p>\n<pre class=\"lang:default decode:true \">public save() {\r\n    let headers = new Headers({ \"Content-Type\": \"application\/json\" });\r\n    let options = new RequestOptions({ headers: headers });\r\n    let url = \"https:\/\/localhost:3000\/person\/\";\r\n    if(this.id) {\r\n        url += this.id;\r\n    }\r\n    if(this.input.firstname &amp;&amp; this.input.lastname) {\r\n        this.http.post(url, JSON.stringify(this.input), options)\r\n            .map(result =&gt; result.json())\r\n            .subscribe(result =&gt; {\r\n                this.location.back();\r\n            });\r\n    }\r\n}<\/pre>\n<p>If an id is present, we&#8217;ll pass it with our POST request. The POST request will have the serialized form data. Provided the request was successful, we can navigate to the previous screen.<\/p>\n<p>The HTML that goes with this TypeScript logic can be found in the project&#8217;s\u00a0<strong>src\/app\/alter\/alter.component.html<\/strong> file and it looks like the following:<\/p>\n<pre class=\"lang:default decode:true \">&lt;form&gt;\r\n    &lt;input type=\"text\" name=\"firstname\" placeholder=\"First Name\" [(ngModel)]=\"input.firstname\" \/&gt;\r\n    &lt;input type=\"text\" name=\"lastname\" placeholder=\"Last Name\" [(ngModel)]=\"input.lastname\" \/&gt;\r\n    &lt;input type=\"text\" name=\"website\" placeholder=\"Website\" [(ngModel)]=\"input.social_media.website\" \/&gt;\r\n    &lt;input type=\"text\" name=\"twitter\" placeholder=\"Twitter\" [(ngModel)]=\"input.social_media.twitter\" \/&gt;\r\n    &lt;button type=\"button\" (click)=\"save()\"&gt;Save&lt;\/button&gt;\r\n&lt;\/form&gt;<\/pre>\n<p>I&#8217;m not much of an artist, so this HTML will be very basic and not styled at all. Notice that each form element is bound to our public\u00a0<code>input<\/code> object.<\/p>\n<p>The second and final component to the project is for listing any saved documents. Open the project&#8217;s\u00a0<strong>src\/app\/list\/list.component.ts<\/strong> file and include the following TypeScript code:<\/p>\n<pre class=\"lang:default decode:true \">import { Component, OnInit } from \"@angular\/core\";\r\nimport { Http } from \"@angular\/http\";\r\nimport { Location } from \"@angular\/common\";\r\nimport \"rxjs\/Rx\";\r\n\r\n@Component({\r\n    moduleId: module.id,\r\n    templateUrl: \"list.component.html\"\r\n})\r\nexport class ListComponent implements OnInit {\r\n\r\n    public people: Array&lt;any&gt;;\r\n\r\n    public constructor(private http: Http, private location: Location) {\r\n        this.people = [];\r\n    }\r\n\r\n    public ngOnInit() {\r\n        this.location.subscribe(() =&gt; {\r\n            this.getPeople();\r\n        });\r\n        this.getPeople();\r\n    }\r\n\r\n    private getPeople() {\r\n        this.http.get(\"https:\/\/localhost:3000\/people\")\r\n            .map(result =&gt; result.json())\r\n            .subscribe(result =&gt; {\r\n                this.people = result;\r\n            });\r\n    }\r\n\r\n    private delete(id: string) {\r\n        this.http.delete(\"https:\/\/localhost:3000\/person\/\" + id)\r\n            .map(result =&gt; result.json())\r\n            .subscribe(result =&gt; {\r\n                for(let i = 0; i &lt; this.people.length; i++) {\r\n                    if(this.people[i]._id == id) {\r\n                        this.people.splice(i, 1);\r\n                        break;\r\n                    }\r\n                }\r\n            });\r\n    }\r\n\r\n}<\/pre>\n<p>Again, we&#8217;ll break it down.<\/p>\n<p>Within the\u00a0<code>constructor<\/code> method we initialize our public variables:<\/p>\n<pre class=\"lang:default decode:true \">public constructor(private http: Http, private location: Location) {\r\n    this.people = [];\r\n}<\/pre>\n<p>In this case, the public variable is our list of documents that will display on the screen. The next step is to query for anyone who might exist and populate the now empty array:<\/p>\n<pre class=\"lang:default decode:true \">private getPeople() {\r\n    this.http.get(\"https:\/\/localhost:3000\/people\")\r\n        .map(result =&gt; result.json())\r\n        .subscribe(result =&gt; {\r\n            this.people = result;\r\n        });\r\n}<\/pre>\n<p>By now you might have noticed that the frontend is mostly just a bunch of HTTP requests to our API. The backend does much of the heavy lifting.<\/p>\n<p>We need to query for documents both when the application loads and when we navigate backwards in the Angular navigation stack. This can be seen in the\u00a0<code>ngOnInit<\/code> method:<\/p>\n<pre class=\"lang:default decode:true \">public ngOnInit() {\r\n    this.location.subscribe(() =&gt; {\r\n        this.getPeople();\r\n    });\r\n    this.getPeople();\r\n}<\/pre>\n<p>We need to subscribe to the location listener so we can determine when we&#8217;ve navigated backwards. We do this because the\u00a0<code>constructor<\/code> and\u00a0<code>ngOnInit<\/code> methods trigger only when navigated to, not navigated back to.<\/p>\n<p>This takes us to the removal of documents:<\/p>\n<pre class=\"lang:default decode:true \">private delete(id: string) {\r\n    this.http.delete(\"https:\/\/localhost:3000\/person\/\" + id)\r\n        .map(result =&gt; result.json())\r\n        .subscribe(result =&gt; {\r\n            for(let i = 0; i &lt; this.people.length; i++) {\r\n                if(this.people[i]._id == id) {\r\n                    this.people.splice(i, 1);\r\n                    break;\r\n                }\r\n            }\r\n        });\r\n}<\/pre>\n<p>In the HTML we&#8217;ll have a button next to every list item. This button will allow us to remove documents by calling the\u00a0<code>delete<\/code> method and passing in the particular document id. If successful, the item will be removed from the database and locally in the array.<\/p>\n<p>The HTML behind this logic can be found in the project&#8217;s\u00a0<strong>src\/app\/list\/list.component.html<\/strong> and it looks something like this:<\/p>\n<pre class=\"lang:default decode:true \">&lt;a [routerLink]=\"['\/alter']\"&gt;New&lt;\/a&gt;\r\n&lt;ul&gt;\r\n    &lt;li *ngFor=\"let person of people\"&gt;\r\n        &lt;a [routerLink]=\"['\/alter', person._id]\"&gt;{{ person.firstname }}&lt;\/a&gt; - &lt;a style=\"cursor: pointer\" (click)=\"delete(person._id)\"&gt;Delete&lt;\/a&gt;\r\n    &lt;\/li&gt;\r\n&lt;\/ul&gt;<\/pre>\n<p>The\u00a0<code>routerLink<\/code> attribute will navigate us to the page for creating or updating data. Then we loop through our array, presenting each document on the screen. If the item is clicked, we navigate to the update page and pass the id of the document that was clicked. This allows us to pre-populate the form. Otherwise, if we want to delete a document, we click the other button.<\/p>\n<p>Even though the core content is done with the Angular side of things, we need to bring it together via the Angular Router.<\/p>\n<p>Open the project&#8217;s\u00a0<strong>src\/app\/app.module.ts<\/strong> file and include the following:<\/p>\n<pre class=\"lang:default decode:true \">import { BrowserModule } from '@angular\/platform-browser';\r\nimport { NgModule } from '@angular\/core';\r\nimport { RouterModule } from \"@angular\/router\";\r\nimport { HttpModule } from \"@angular\/http\";\r\nimport { FormsModule } from \"@angular\/forms\";\r\n\r\nimport { AppComponent } from '.\/app.component';\r\nimport { ListComponent } from \".\/list\/list.component\";\r\nimport { AlterComponent } from \".\/alter\/alter.component\";\r\n\r\nconst routes = [\r\n    { path: \"\", component: ListComponent },\r\n    { path: \"alter\", component: AlterComponent },\r\n    { path: \"alter\/:id\", component: AlterComponent }\r\n];\r\n\r\n@NgModule({\r\n    declarations: [\r\n        AppComponent,\r\n        ListComponent,\r\n        AlterComponent\r\n    ],\r\n    imports: [\r\n        BrowserModule,\r\n        HttpModule,\r\n        RouterModule,\r\n        FormsModule,\r\n        RouterModule.forRoot(routes)\r\n    ],\r\n    providers: [],\r\n    bootstrap: [AppComponent]\r\n})\r\nexport class AppModule { }<\/pre>\n<p>Notice we&#8217;ve just imported our components and defined a route for each of them. Nothing too fancy going on there.<\/p>\n<p>Depending on your version of the Angular CLI, you might need to alter the project&#8217;s\u00a0<strong>src\/app\/app.component.html<\/strong>\u00a0file to contain the following:<\/p>\n<pre class=\"lang:default decode:true \">&lt;router-outlet&gt;&lt;\/router-outlet&gt;<\/pre>\n<p>This allows navigation to work in the project.<\/p>\n<h2>Conclusion<\/h2>\n<p>You just saw the OCEAN stack and CEAN stack material that I presented on at the <a href=\"https:\/\/www.meetup.com\/AngularJS-OC\/events\/240799354\/\" target=\"_blank\" rel=\"noopener noreferrer\">Angular Orange County<\/a> group and the <a href=\"https:\/\/www.meetup.com\/Couchbase-Los-Angeles\/events\/241028595\/\" target=\"_blank\" rel=\"noopener noreferrer\">Couchbase Los Angeles<\/a> group.<\/p>\n<p>If you&#8217;re a MongoDB developer and looking into Couchbase for the first time, you might have a look at a <a href=\"https:\/\/labs.couchbase.com\/mongodb-to-couchbase-cookbook\/\" target=\"_blank\" rel=\"noopener noreferrer\">cookbook<\/a> that I wrote that dives deeper into Ottoman.js and N1QL while explaining how to move from MongoDB.<\/p>\n<p>For more information on Couchbase and Node.js, check out the <a href=\"https:\/\/www.couchbase.com\/developers\/\" target=\"_blank\" rel=\"noopener noreferrer\">Couchbase Developer Portal<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was recently at two different Meetup groups in Southern California presenting on what I&#8217;m calling, the OCEAN stack, which is composed of Ottoman.js, Couchbase Server, Express Framework, Angular, and Node.js. We had a great turnout of developers hungry to [&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":[1815,1816,1822,1812],"tags":[1719,1349,1725],"ppma_author":[9032],"class_list":["post-3841","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-best-practices-and-tutorials","category-couchbase-server","category-node-js","category-n1ql-query","tag-cean","tag-development","tag-nosql-database"],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v27.3 (Yoast SEO v27.3) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Couchbase Meetup Project on the OCEAN Stack Available<\/title>\n<meta name=\"description\" content=\"See how to build an API using Ottoman.js and then with N1QL instead of Ottoman.js. Both APIs will be consumed by a front-end developed in Angular.\" \/>\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\/pt\/couchbase-meetup-project-ocean-stack-available\/\" \/>\n<meta property=\"og:locale\" content=\"pt_BR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Couchbase Meetup Project on the OCEAN Stack Available\" \/>\n<meta property=\"og:description\" content=\"See how to build an API using Ottoman.js and then with N1QL instead of Ottoman.js. Both APIs will be consumed by a front-end developed in Angular.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/pt\/couchbase-meetup-project-ocean-stack-available\/\" \/>\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=\"2017-07-20T14:00:11+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T01:46:03+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/07\/oc-angular-meetup.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"1200\" \/>\n\t<meta property=\"og:image:height\" content=\"549\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\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=\"9 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\\\/\\\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/#article\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/\"},\"author\":{\"name\":\"Nic Raboy, Developer Advocate, Couchbase\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#\\\/schema\\\/person\\\/bb545ebe83bb2d12f91095811d0a72e1\"},\"headline\":\"Couchbase Meetup Project on the OCEAN Stack Available\",\"datePublished\":\"2017-07-20T14:00:11+00:00\",\"dateModified\":\"2025-06-14T01:46:03+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/\"},\"wordCount\":2004,\"commentCount\":0,\"publisher\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/1\\\/2022\\\/11\\\/couchbase-nosql-dbaas.png\",\"keywords\":[\"CEAN\",\"Development\",\"NoSQL Database\"],\"articleSection\":[\"Best Practices and Tutorials\",\"Couchbase Server\",\"Node.js\",\"SQL++ \\\/ N1QL Query\"],\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/\",\"name\":\"Couchbase Meetup Project on the OCEAN Stack Available\",\"isPartOf\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/#primaryimage\"},\"image\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/#primaryimage\"},\"thumbnailUrl\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/wp-content\\\/uploads\\\/sites\\\/1\\\/2022\\\/11\\\/couchbase-nosql-dbaas.png\",\"datePublished\":\"2017-07-20T14:00:11+00:00\",\"dateModified\":\"2025-06-14T01:46:03+00:00\",\"description\":\"See how to build an API using Ottoman.js and then with N1QL instead of Ottoman.js. Both APIs will be consumed by a front-end developed in Angular.\",\"breadcrumb\":{\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/#breadcrumb\"},\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/couchbase-meetup-project-ocean-stack-available\\\/#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\\\/couchbase-meetup-project-ocean-stack-available\\\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Couchbase Meetup Project on the OCEAN Stack Available\"}]},{\"@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\":\"pt-BR\"},{\"@type\":\"Organization\",\"@id\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\\\/\\\/www.couchbase.com\\\/blog\\\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@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\":\"pt-BR\",\"@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\\\/pt\\\/author\\\/nic-raboy-2\\\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Projeto Meetup do Couchbase na pilha OCEAN dispon\u00edvel","description":"Veja como criar uma API usando Ottoman.js e depois com N1QL em vez de Ottoman.js. Ambas as APIs ser\u00e3o consumidas por um front-end desenvolvido em Angular.","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\/pt\/couchbase-meetup-project-ocean-stack-available\/","og_locale":"pt_BR","og_type":"article","og_title":"Couchbase Meetup Project on the OCEAN Stack Available","og_description":"See how to build an API using Ottoman.js and then with N1QL instead of Ottoman.js. Both APIs will be consumed by a front-end developed in Angular.","og_url":"https:\/\/www.couchbase.com\/blog\/pt\/couchbase-meetup-project-ocean-stack-available\/","og_site_name":"The Couchbase Blog","article_author":"https:\/\/www.facebook.com\/thepolyglotdeveloper","article_published_time":"2017-07-20T14:00:11+00:00","article_modified_time":"2025-06-14T01:46:03+00:00","og_image":[{"width":1200,"height":549,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2017\/07\/oc-angular-meetup.jpg","type":"image\/jpeg"}],"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":"9 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/"},"author":{"name":"Nic Raboy, Developer Advocate, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/bb545ebe83bb2d12f91095811d0a72e1"},"headline":"Couchbase Meetup Project on the OCEAN Stack Available","datePublished":"2017-07-20T14:00:11+00:00","dateModified":"2025-06-14T01:46:03+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/"},"wordCount":2004,"commentCount":0,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","keywords":["CEAN","Development","NoSQL Database"],"articleSection":["Best Practices and Tutorials","Couchbase Server","Node.js","SQL++ \/ N1QL Query"],"inLanguage":"pt-BR","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/","url":"https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/","name":"Projeto Meetup do Couchbase na pilha OCEAN dispon\u00edvel","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2022\/11\/couchbase-nosql-dbaas.png","datePublished":"2017-07-20T14:00:11+00:00","dateModified":"2025-06-14T01:46:03+00:00","description":"Veja como criar uma API usando Ottoman.js e depois com N1QL em vez de Ottoman.js. Ambas as APIs ser\u00e3o consumidas por um front-end desenvolvido em Angular.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/#breadcrumb"},"inLanguage":"pt-BR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/"]}]},{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/couchbase-meetup-project-ocean-stack-available\/#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\/couchbase-meetup-project-ocean-stack-available\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Couchbase Meetup Project on the OCEAN Stack Available"}]},{"@type":"WebSite","@id":"https:\/\/www.couchbase.com\/blog\/#website","url":"https:\/\/www.couchbase.com\/blog\/","name":"Blog do Couchbase","description":"Couchbase, o banco de dados NoSQL","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":"pt-BR"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"Blog do Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"pt-BR","@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, defensor dos desenvolvedores, Couchbase","image":{"@type":"ImageObject","inLanguage":"pt-BR","@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 \u00e9 um defensor das modernas tecnologias de desenvolvimento m\u00f3vel e da Web. Ele tem experi\u00eancia em Java, JavaScript, Golang e uma variedade de estruturas, como Angular, NativeScript e Apache Cordova. Nic escreve sobre suas experi\u00eancias de desenvolvimento relacionadas a tornar o desenvolvimento m\u00f3vel e da Web mais f\u00e1cil de entender.","sameAs":["https:\/\/www.thepolyglotdeveloper.com","https:\/\/www.facebook.com\/thepolyglotdeveloper","https:\/\/x.com\/nraboy"],"url":"https:\/\/www.couchbase.com\/blog\/pt\/author\/nic-raboy-2\/"}]}},"acf":[],"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","0":null,"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/3841","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/users\/63"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/comments?post=3841"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/3841\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media\/13873"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media?parent=3841"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/categories?post=3841"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/tags?post=3841"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/ppma_author?post=3841"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}