{"id":5538,"date":"2018-07-26T15:42:30","date_gmt":"2018-07-26T22:42:30","guid":{"rendered":"https:\/\/www.couchbase.com\/blog\/?p=5538"},"modified":"2025-06-13T21:23:10","modified_gmt":"2025-06-14T04:23:10","slug":"tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server","status":"publish","type":"post","link":"https:\/\/www.couchbase.com\/blog\/pt\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/","title":{"rendered":"Tutorial: Criar um aplicativo de pontos de interesse com Vue.js, Node.js, Express e Couchbase Server"},"content":{"rendered":"<p><span class=\"image\"><a class=\"image\" title=\"https:\/\/www.couchbase.com\/downloads\" href=\"https:\/\/www.couchbase.com\/blog\/pt\/downloads\/\"><img decoding=\"async\" src=\"https:\/\/img.shields.io\/badge\/Couchbase-v5.5.0-red.svg\" alt=\"Couchbase v5.5.0 red\" \/><\/a><\/span> <span class=\"image\"><a class=\"image\" title=\"https:\/\/opensource.org\/licenses\/Apache-2.0\" href=\"https:\/\/opensource.org\/licenses\/Apache-2.0\"><img decoding=\"async\" src=\"https:\/\/img.shields.io\/badge\/License-Apache%202.0-green.svg\" alt=\"License Apache%202.0 green\" \/><\/a><\/span><\/p>\n<div id=\"preamble\">\n<div class=\"sectionbody\">\n<h2 id=\"_introduction\" class=\"discrete\">Introdu\u00e7\u00e3o<\/h2>\n<div class=\"paragraph\">\n<p>Neste tutorial, criaremos um aplicativo de pilha completa usando <a title=\"https:\/\/vuejs.org\/\" href=\"https:\/\/vuejs.org\">Vue.js<\/a>, <a title=\"https:\/\/nodejs.org\/\" href=\"https:\/\/nodejs.org\/\">Node.js<\/a>, <a title=\"https:\/\/expressjs.com\/\" href=\"https:\/\/expressjs.com\">Expresso<\/a>e <a title=\"https:\/\/www.couchbase.com\/\" href=\"https:\/\/www.couchbase.com\/blog\/pt\/\">Servidor Couchbase<\/a>. Al\u00e9m desses frameworks, usaremos o <a title=\"https:\/\/developers.google.com\/maps\/documentation\/\" href=\"https:\/\/developers.google.com\/maps\/documentation\/\">Google Maps<\/a> e <a title=\"https:\/\/developer.here.com\/develop\/rest-apis\" href=\"https:\/\/developer.here.com\/develop\/rest-apis\">Aqui Lugares<\/a> APIs REST.<\/p>\n<\/div>\n<div id=\"toc\" class=\"toc\">\n<div id=\"toctitle\" class=\"title\">\n<div class=\"paragraph\">\n<p><strong>Para mais informa\u00e7\u00f5es<\/strong> clonar o reposit\u00f3rio <a title=\"https:\/\/github.com\/couchbaselabs\/points-of-interest:\" href=\"https:\/\/github.com\/couchbaselabs\/points-of-interest:\">aqui<\/a>.<\/p>\n<\/div>\n<p>Conte\u00fado<\/p>\n<\/div>\n<ul class=\"sectlevel1\">\n<li><a title=\"\" href=\"#_what_well_build\">O que vamos construir<\/a><\/li>\n<li><a title=\"\" href=\"#_what_you_need\">O que voc\u00ea precisa<\/a><\/li>\n<li><a title=\"\" href=\"#_getting_started\">Primeiros passos<\/a><\/li>\n<li><a title=\"\" href=\"#_the_web_client_skeleton\">O esqueleto do cliente Web<\/a>\n<ul class=\"sectlevel2\">\n<li><a title=\"\" href=\"#_generating_the_vue_js_client_scaffolding\">Gera\u00e7\u00e3o do andaime do cliente Vue.js<\/a><\/li>\n<li><a title=\"\" href=\"#_restructuring_and_fixup\">Reestrutura\u00e7\u00e3o e corre\u00e7\u00e3o<\/a><\/li>\n<li><a title=\"\" href=\"#_install_dependencies_and_build\">Instalar depend\u00eancias e compilar<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a title=\"\" href=\"#_the_web_server_skeleton\">O esqueleto do servidor web<\/a><\/li>\n<li><a title=\"\" href=\"#_fleshing_out_the_client_and_server\">Desenvolvendo o cliente e o servidor<\/a>\n<ul class=\"sectlevel2\">\n<li><a title=\"\" href=\"#_the_web_client_code\">O c\u00f3digo do cliente Web<\/a><\/li>\n<li><a title=\"\" href=\"#_the_web_server_code\">O c\u00f3digo do servidor web<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a title=\"\" href=\"#_the_couchbase_server_eventing_service_code\">C\u00f3digo do servi\u00e7o de eventos do Couchbase Server<\/a>\n<ul class=\"sectlevel2\">\n<li><a title=\"\" href=\"#_eventing_meta_data_bucket\">Balde de metadados de eventos<\/a><\/li>\n<li><a title=\"\" href=\"#_adding_a_function\">Adi\u00e7\u00e3o de uma fun\u00e7\u00e3o<\/a><\/li>\n<li><a title=\"\" href=\"#_deploying_a_function\">Implementa\u00e7\u00e3o de uma fun\u00e7\u00e3o<\/a><\/li>\n<li><a title=\"\" href=\"#_understanding_the_function_code\">Entendendo o c\u00f3digo de fun\u00e7\u00e3o<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a title=\"\" href=\"#_final_steps\">Etapas finais<\/a><\/li>\n<li><a title=\"\" href=\"#_source\">Fonte<\/a><\/li>\n<li><a title=\"\" href=\"#_webinar\">Webinar<\/a><\/li>\n<li><a title=\"\" href=\"#_postscript\">P\u00f3s-escrito<\/a><\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_what_well_build\">O que vamos construir<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Vamos criar um aplicativo da Web de p\u00e1gina \u00fanica que mostra pontos de interesse (POI) em torno de hot\u00e9is selecionados em uma lista de cidades. Os POIs ser\u00e3o exibidos em um mapa interativo do Google. Aqui est\u00e1 uma anima\u00e7\u00e3o mostrando os resultados finais.<\/p>\n<\/div>\n<div class=\"imageblock\">\n<div class=\"content\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/couchbaselabs\/blog-source-code\/master\/Greeley\/0048POI\/images\/POI.gif\" alt=\"Animated Application Demo\" \/><\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>H\u00e1 algumas reviravoltas extras para mostrar algumas t\u00e9cnicas mais avan\u00e7adas.<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>As cidades s\u00e3o escolhidas combinando aeroportos que tenham hot\u00e9is pr\u00f3ximos na mesma cidade.<\/li>\n<li>Recuperamos os POIs usando uma chamada REST, mas os salvamos em nosso banco de dados.<\/li>\n<li>O lado do cliente recebe dados por meio de pushes usando <a title=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Server-sent_events\/Using_server-sent_events\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Server-sent_events\/Using_server-sent_events\">eventos enviados pelo servidor<\/a>.<\/li>\n<\/ul>\n<\/div>\n<div class=\"paragraph\">\n<p>Embora o c\u00f3digo seja curto, ele mostra v\u00e1rias t\u00e9cnicas com os recursos de depend\u00eancia de propriedade e vincula\u00e7\u00e3o de dados reativos do Vue. Combinado com alguns recursos avan\u00e7ados do Couchbase, teremos um aplicativo bom e funcional com pouco trabalho.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_what_you_need\">O que voc\u00ea precisa<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>O aplicativo foi desenvolvido inteiramente em JavaScript. Voc\u00ea precisa de apenas alguns elementos para come\u00e7ar.<\/p>\n<\/div>\n<div class=\"olist arabic\">\n<ol class=\"arabic\" start=\"https:\/\/www.couchbase.com\/get-started\">\n<li><a title=\"https:\/\/nodejs.org\/\" href=\"https:\/\/nodejs.org\/\">Node.js<\/a> instalado<\/li>\n<li><a title=\"https:\/\/www.couchbase.com\/downloads\" href=\"https:\/\/www.couchbase.com\/blog\/pt\/downloads\/\">Servidor Couchbase<\/a> 5.5.0 ou posterior instalado<\/li>\n<\/ol>\n<\/div>\n<div class=\"paragraph\">\n<p>Voc\u00ea tamb\u00e9m precisar\u00e1 obter chaves para o <a title=\"https:\/\/developers.google.com\/maps\/documentation\/javascript\/get-api-key\" href=\"https:\/\/developers.google.com\/maps\/documentation\/javascript\/get-api-key\">API JavaScript do Google Maps<\/a> e o <a title=\"https:\/\/developer.here.com\/develop\/rest-apis\" href=\"https:\/\/developer.here.com\/develop\/rest-apis\">APIs REST da HERE<\/a>. Ambos podem ser usados gratuitamente (com limita\u00e7\u00f5es).<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Os dados para o aplicativo s\u00e3o fornecidos como uma amostra incorporada \u00e0 distribui\u00e7\u00e3o do Couchbase Server.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_getting_started\">Primeiros passos<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Criaremos a estrutura do aplicativo come\u00e7ando com o c\u00f3digo do cliente Web. Em seguida, vem o c\u00f3digo Node + Express do lado do servidor. Por fim, examinaremos o lado do servidor Couchbase.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Daremos uma olhada mais detalhada nas consultas N1Ql, incluindo <a title=\"https:\/\/developer.couchbase.com\/documentation\/server\/current\/n1ql\/n1ql-language-reference\/from.html#topic_rnt_zfk_np__section_ek1_jnx_1db\" href=\"https:\/\/developer.couchbase.com\/documentation\/server\/current\/n1ql\/n1ql-language-reference\/from.html#topic_rnt_zfk_np__section_ek1_jnx_1db\">Jun\u00e7\u00e3o com a ANSI<\/a>. Este aplicativo utiliza o novo <a title=\"https:\/\/developer.couchbase.com\/documentation\/server\/current\/eventing\/eventing-overview.html\" href=\"https:\/\/developer.couchbase.com\/documentation\/server\/current\/eventing\/eventing-overview.html\">Servi\u00e7o e fun\u00e7\u00f5es de eventos<\/a>. Para concluir, examinaremos o c\u00f3digo JavaScript.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Para come\u00e7ar, crie um novo diret\u00f3rio no qual voc\u00ea deseja manter o projeto. Abra um prompt de comando e v\u00e1 para esse diret\u00f3rio.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_the_web_client_skeleton\">O esqueleto do cliente Web<\/h2>\n<div class=\"sectionbody\">\n<div class=\"sect2\">\n<h3 id=\"_generating_the_vue_js_client_scaffolding\">Gera\u00e7\u00e3o do andaime do cliente Vue.js<\/h3>\n<div class=\"paragraph\">\n<p>O cliente web usa <a title=\"https:\/\/vuejs.org\/\" href=\"https:\/\/vuejs.org\">Vue.js<\/a>.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Usaremos o Vue CLI para criar o projeto b\u00e1sico para n\u00f3s. Mostrarei uma integra\u00e7\u00e3o f\u00e1cil entre o lado do cliente e do servidor com o <a title=\"https:\/\/webpack.js.org\/\" href=\"https:\/\/webpack.js.org\/\">webpack<\/a>. Para isso, ser\u00e1 necess\u00e1rio reorganizar um pouco os arquivos.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Instale o Vue CLI usando o npm, caso ainda n\u00e3o o tenha.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<pre class=\"lang:sh decode:true\">npm install -g @vue\/cli<\/pre>\n<p>Gosto de usar <a title=\"https:\/\/getbootstrap.com\/\" href=\"https:\/\/getbootstrap.com\/\">Bootstrap<\/a>. H\u00e1 pelo menos alguns projetos que integram o Boostrap ao Vue. Eu escolhi o <a title=\"https:\/\/bootstrap-vue.js.org\/\" href=\"https:\/\/bootstrap-vue.js.org\/\">Bootstrap-Vue<\/a>. Isso n\u00e3o \u00e9 realmente necess\u00e1rio. N\u00e3o \u00e9 muito dif\u00edcil remover essa depend\u00eancia, se voc\u00ea quiser.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Criar o boilerplate do projeto. \u00c9 aqui que entra o modelo simples do Webpack. O modelo <code>inicial<\/code> far\u00e1 algumas perguntas. N\u00e3o h\u00e1 problema em usar os padr\u00f5es.<\/p>\n<pre class=\"lang:sh decode:true\">npm install -g @vue\/cli-init\r\nvue init bootstrap-vue\/webpack-simple client<\/pre>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_restructuring_and_fixup\">Reestrutura\u00e7\u00e3o e corre\u00e7\u00e3o<\/h3>\n<div class=\"paragraph\">\n<p>Agora, v\u00e1 para o diret\u00f3rio do cliente. Mova o diret\u00f3rio <code>package.json<\/code> e <code>.gitignore<\/code> criados em um n\u00edvel superior. Dessa forma, eles ser\u00e3o compartilhados em todo o projeto.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<pre class=\"lang:sh decode:true\">cd client\/\r\nmv package.json .gitignore ..<\/pre>\n<p>A configura\u00e7\u00e3o do webpack tamb\u00e9m tem um pequeno erro. Abrir <code>webpack.config.js<\/code>. Na se\u00e7\u00e3o que come\u00e7a com<\/p>\n<\/div>\n<div class=\"paragraph\">\n<pre class=\"lang:default decode:true\">        test: \/\\.(png|jpg|gif|svg)$\/,<\/pre>\n<p>altere a linha de op\u00e7\u00f5es para que fique<\/p>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<pre class=\"lang:default decode:true\">           name: 'assets\/[name].[ext]?[hash]'<\/pre>\n<h3 id=\"_install_dependencies_and_build\">Instalar depend\u00eancias e compilar<\/h3>\n<div class=\"paragraph\">\n<p>Inicialize e instale as depend\u00eancias b\u00e1sicas.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<pre class=\"lang:sh decode:true\">npm install<\/pre>\n<p>Instale nossas outras depend\u00eancias. Muitas delas s\u00e3o pacotes padr\u00e3o (morgan, body-parser). Eu uso o <a title=\"https:\/\/github.com\/axios\/axios\" href=\"https:\/\/github.com\/axios\/axios\">\u00e1xis<\/a> para chamadas de rede.\u00a0<a title=\"https:\/\/www.npmjs.com\/package\/sse-channel\" href=\"https:\/\/www.npmjs.com\/package\/sse-channel\">canal sse<\/a> \u00e9 um bom pacote de eventos enviados pelo servidor. Ele \u00e9 um pouco mais sofisticado e mais f\u00e1cil de usar do que outros que j\u00e1 experimentei. E h\u00e1 um pacote para facilitar o trabalho com o Google Maps no Vue chamado <a title=\"https:\/\/www.npmjs.com\/package\/vue2-google-maps\" href=\"https:\/\/www.npmjs.com\/package\/vue2-google-maps\">vue2-google-maps<\/a>.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Instale o restante das depend\u00eancias da seguinte forma. Isso inclui o que precisaremos para o servidor.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<pre class=\"lang:sh decode:true\">npm install --save vue2-google-maps axios express sse-channel dotenv morgan debug cookie-parser body-parser bluebird couchbase<\/pre>\n<p>Isso lhe dar\u00e1 um front-end funcional baseado em Vue. Para constru\u00ed-lo, j\u00e1 que mudamos o <code>package.json<\/code> para um n\u00edvel superior, precisamos ajustar a se\u00e7\u00e3o de scripts npm. Editar <code>package.json<\/code> na raiz do projeto e altere a linha de compila\u00e7\u00e3o para<\/p>\n<\/div>\n<div class=\"paragraph\">\n<pre class=\"lang:default decode:true\">    \"build\": \"cd client &amp;&amp; cross-env NODE_ENV=production webpack --progress --hide-modules &amp;&amp; cp index.html dist\/\"<\/pre>\n<p>Agora, no cliente, fa\u00e7a <code>npm run build<\/code>.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Voc\u00ea pode abrir o <code>index.html<\/code> agora, mas ele n\u00e3o funcionar\u00e1. Vamos pular para a cria\u00e7\u00e3o do servidor, ou voc\u00ea pode tentar corrigir o problema aqui se quiser apenas ver o cliente aut\u00f4nomo.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_the_web_server_skeleton\">O esqueleto do servidor web<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Navegue de volta para a raiz do projeto e prepare o diret\u00f3rio do servidor.<\/p>\n<pre class=\"lang:sh decode:true\">mkdir server\r\ncd server<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>Vamos criar o servidor diretamente. Inicie o aplicativo b\u00e1sico editando um novo arquivo <code>app.js<\/code>. Cole o seguinte e salve.<\/p>\n<pre class=\"lang:js decode:true\">const express = require('express');\r\n\r\nconst debug = require('debug')('poi:server');\r\nconst path = require('path');\r\nconst logger = require('morgan');\r\nconst cookieParser = require('cookie-parser');\r\nconst bodyParser = require('body-parser');\r\nconst http = require('http');\r\n\r\nconst app = express();\r\n\r\napp.use(logger('dev'));\r\napp.use(bodyParser.json());\r\napp.use(bodyParser.urlencoded({ extended: false }));\r\napp.use(cookieParser());\r\n\r\napp.use(function(req, res, next) {\r\n  res.header(\"Access-Control-Allow-Origin\", \"*\");\r\n  res.header(\"Access-Control-Allow-Headers\", \"Origin, X-Requested-With, Content-Type, Accept\");\r\n  next();\r\n});\r\n\r\napp.use(express.static(path.join(__dirname, '..\/client')));\r\n\r\n\/\/ catch 404 and forward to error handler\r\napp.use(function(req, res, next) {\r\n  console.dir(req);\r\n  console.dir(res);\r\n  let err = new Error('Not Found');\r\n  err.status = 404;\r\n  next(err);\r\n});\r\n\r\n\/\/ error handler\r\napp.use(function(err, req, res, next) {\r\n  \/\/ set locals, only providing error in development\r\n  res.locals.message = err.message;\r\n  res.locals.error = req.app.get('env') === 'development' ? err : {};\r\n\r\n  \/\/ render the error page\r\n  res.status(err.status || 500);\r\n  res.render('error');\r\n});\r\n\r\n\/\/ HTTP server\r\nhttp.createServer(app).listen(8080);<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>Esta \u00e9 uma vers\u00e3o simplificada da final. Ela serve apenas o cliente padr\u00e3o que criamos anteriormente.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Nesse ponto, voc\u00ea deve ser capaz de executar <code>n\u00f3 app.js<\/code> no diret\u00f3rio do servidor. Abra uma guia do navegador e navegue at\u00e9 <code><a class=\"bare\" title=\"https:\/\/localhost:8080\/\" href=\"https:\/\/localhost:8080\">https:\/\/localhost:8080<\/a><\/code>. Voc\u00ea dever\u00e1 ver algo parecido com isto.<\/p>\n<\/div>\n<div class=\"imageblock\">\n<div class=\"content\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/couchbaselabs\/blog-source-code\/master\/Greeley\/0048POI\/images\/BaseVueClient.png\" alt=\"Vue Client Template\" \/><\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_fleshing_out_the_client_and_server\">Desenvolvendo o cliente e o servidor<\/h2>\n<div class=\"sectionbody\">\n<div class=\"sect2\">\n<h3 id=\"_the_web_client_code\">O c\u00f3digo do cliente Web<\/h3>\n<div class=\"paragraph\">\n<p>Agora, voltaremos e criaremos o cliente real. No diret\u00f3rio do cliente, no subdiret\u00f3rio <code>src<\/code>, abra o arquivo <code>App.vue<\/code>. Atualize-o da seguinte forma.<\/p>\n<pre class=\"lang:default decode:true\" data-url=\"https:\/\/raw.githubusercontent.com\/couchbaselabs\/points-of-interest\/master\/client\/src\/App.vue\">&lt;template&gt;\r\n  &lt;div id=\"app\"&gt;\r\n    &lt;div class=\"container\"&gt;\r\n      &lt;div class=\"row justify-content-center\"&gt;\r\n        &lt;div class=\"col-12\"&gt;\r\n          &lt;h2&gt;Points of Interest&lt;\/h2&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div class=\"col-md-12\"&gt;\r\n          &lt;b-dropdown id=\"cities\" v-bind:text=display class=\"m-md-2\"&gt;\r\n            &lt;b-dropdown-item-button v-for=\"city in cities\" v-bind:key=\"city.name\" v-on:click=\"selected = city\"&gt;{{ city.name }}&lt;\/b-dropdown-item-button&gt;\r\n          &lt;\/b-dropdown&gt;\r\n          &lt;b-table id=\"destinations\" :items=\"destinationsProvider\" :fields=\"fields\" @row-clicked=\"hotelSelected\" striped hover&gt;&lt;\/b-table&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div class=\"col-md-12\"&gt;\r\n          &lt;GmapMap ref=\"map\" style=\"width: 100%; height: 400px;\" :zoom=\"16\" :center=\"{lat: 43.542619, lng: 6.955665}\"&gt;\r\n            &lt;GmapMarker v-for=\"(marker, index) in poi\"\r\n              :key=\"index\"\r\n              :position=\"{ lat: marker.position[0], lng: marker.position[1] }\"\r\n              :icon=\"{ url: marker.icon }\"\r\n            \/&gt;\r\n          &lt;\/GmapMap&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div class=\"col-8\" \/&gt;\r\n        &lt;div class=\"col-4\" id=\"tagline\"&gt;\r\n            Powered by &lt;img src=\".\/assets\/logo.png\"&gt;\r\n        &lt;\/div&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/div&gt;\r\n&lt;\/template&gt;\r\n\r\n&lt;script&gt;\r\nimport axios from 'axios'\r\n\r\nconst serverURL = location.origin;\r\nconst server = axios.create({ baseURL: serverURL });\r\nconst es = new EventSource(`${serverURL}\/events\/poi`);\r\n\r\nexport default {\r\n  name: 'app',\r\n  data() {\r\n    return {\r\n      fields: [\r\n        { key: 'name', label: 'Hotel Name', sortable: true },\r\n        { key: 'address', sortable: false },\r\n        { key: 'airportname', label: 'Airport Name', sortable: true },\r\n        { key: 'icao', label: 'ICAO Code', sortable: true }\r\n      ],\r\n      selected: null,\r\n      cities: [],\r\n      poi: []\r\n    }\r\n  },\r\n  computed: {\r\n    display: function() {\r\n      return this.selected ? this.selected.name : 'Choose a city';\r\n    }\r\n  },\r\n  watch: {\r\n    selected: function() {\r\n      this.$root.$emit('bv::refresh::table', 'destinations');\r\n    }\r\n  },\r\n  methods: {\r\n    destinationsProvider(context) {\r\n      if (null === this.selected) return [];\r\n\r\n      let promise = server.get(`\/records\/hotels\/byCity\/${this.selected.name}`);\r\n\r\n      return promise.then(response =&gt; {\r\n        return(response.data);\r\n      }).catch(error =&gt; {\r\n        return [];\r\n      });\r\n    },\r\n    hotelSelected(record, index) {\r\n      this.$refs.map.panTo({ lat: record.geo.lat, lng: record.geo.lon });\r\n\r\n      server.post('\/records\/select\/geo', record.geo)\r\n      .catch(error =&gt; {\r\n        console.log(error)\r\n      });\r\n    }\r\n  },\r\n  mounted: function() {\r\n    es.addEventListener('poi', event =&gt; this.poi = JSON.parse(event.data));\r\n\r\n    server.get('\/records\/destinations')\r\n    .then(response =&gt; {\r\n      this.cities = response.data;\r\n    })\r\n    .catch(error =&gt; {\r\n      console.log(error)\r\n    });\r\n  }\r\n}\r\n&lt;\/script&gt;\r\n\r\n&lt;style&gt;\r\n#app {\r\n  font-family: 'Avenir', Helvetica, Arial, sans-serif;\r\n  -webkit-font-smoothing: antialiased;\r\n  -moz-osx-font-smoothing: grayscale;\r\n  text-align: center;\r\n  color: #2c3e50;\r\n  margin-top: 60px;\r\n}\r\n\r\n#tagline img {\r\n  height: 38px;\r\n  margin: 10px;\r\n}\r\n\r\nh1, h2 {\r\n  font-weight: normal;\r\n}\r\n\r\nul {\r\n  list-style-type: none;\r\n  padding: 0;\r\n}\r\n\r\nli {\r\n  display: inline-block;\r\n  margin: 0 10px;\r\n}\r\n\r\na {\r\n  color: #42b983;\r\n}\r\n&lt;\/style&gt;<\/pre>\n<p>Essa \u00e9 a maior parte do c\u00f3digo do lado do cliente.<\/p>\n<p>N\u00e3o entrarei em detalhes sobre a se\u00e7\u00e3o do modelo ou o CSS. Vou destacar um elemento interessante. A API Here retorna, entre outras coisas, links para \u00edcones adequados para uso no Maps. Se voc\u00ea acompanhar o fluxo, ver\u00e1 que os marcadores de mapa est\u00e3o carregando esses \u00edcones diretamente usando os URLs de inclus\u00e3o.<\/p>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_wiring_up_the_vue_databinding\">Conex\u00e3o da liga\u00e7\u00e3o de dados do Vue<\/h4>\n<div class=\"paragraph\">\n<p>Percorrendo a se\u00e7\u00e3o do script, voc\u00ea ver\u00e1 que fa\u00e7o uso intenso dos recursos reativos do Vue. Para entender essa parte, ser\u00e1 \u00fatil se voc\u00ea tiver pelo menos alguma familiaridade com o Vue, especialmente <a title=\"https:\/\/vuejs.org\/v2\/guide\/computed.html\" href=\"https:\/\/vuejs.org\/v2\/guide\/computed.html\">propriedades computadas e observadores<\/a>, <a title=\"https:\/\/vuejs.org\/v2\/guide\/instance.html\" href=\"https:\/\/vuejs.org\/v2\/guide\/instance.html\">dados, m\u00e9todo e ganchos de ciclo de vida<\/a>.<\/p>\n<p>Fazemos uso do <code>montado<\/code> para adicionar um ouvinte para eventos enviados pelo servidor e para preencher inicialmente a lista suspensa de cidades. O trabalho mais pesado da l\u00f3gica comercial aqui acontece na consulta ao banco de dados, como veremos.<\/p>\n<p>Vamos acompanhar como funciona a sele\u00e7\u00e3o de uma cidade. Observe que cada item no menu suspenso do bot\u00e3o tem um ouvinte de clique vinculado que define <code>selecionado<\/code> para os dados da cidade para essa entrada. Temos um m\u00e9todo watch definido em <code>selecionado<\/code>. O Vue tamb\u00e9m sabe automaticamente que a propriedade computada <code>exibi\u00e7\u00e3o<\/code> depende de <code>selecionado<\/code>.<\/p>\n<p>Isso significa que sempre que uma cidade \u00e9 selecionada por meio do menu suspenso, temos uma cascata de atividades. Mudan\u00e7a <code>selecionado<\/code> causas <code>exibi\u00e7\u00e3o<\/code> a ser recalculado. Isso, por sua vez, define o texto do bot\u00e3o suspenso, j\u00e1 que ele est\u00e1 vinculado a <code>exibi\u00e7\u00e3o<\/code>. O <code>selecionado<\/code>no m\u00e9todo <code>assistir<\/code> aciona uma atualiza\u00e7\u00e3o da tabela de listagem de hot\u00e9is sempre que uma nova cidade \u00e9 selecionada.<\/p>\n<p>A tabela <code>itens<\/code> est\u00e3o vinculados a <code>destinationsProvider<\/code> sob <code>m\u00e9todos<\/code>. A atualiza\u00e7\u00e3o da tabela faz com que esse c\u00f3digo seja executado. Como a lista de cidades original, ele extrai os hot\u00e9is por meio de uma chamada ass\u00edncrona ao nosso banco de dados por meio de um endpoint REST do servidor.<\/p>\n<p>O Vue cuida de muitas coisas aqui para n\u00f3s. Por exemplo, a chamada para atualizar a tabela n\u00e3o recebe dados imediatamente. O Vue renderizar\u00e1 novamente as partes relevantes do DOM automaticamente sempre que a chamada REST retornar. N\u00e3o precisamos fornecer nenhum dos cabos, a n\u00e3o ser especificar a liga\u00e7\u00e3o entre <code>itens<\/code> e <code>provedor de destino<\/code>.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_completing_the_web_client\">Conclus\u00e3o do cliente Web<\/h4>\n<div class=\"paragraph\">Para finalizar o lado do cliente, temos algumas outras etapas curtas a serem executadas. Precisamos importar o m\u00f3dulo para ajudar com o Google Maps e fornecer uma chave. Editar <code>main.js<\/code>. Adicione uma linha de importa\u00e7\u00e3o e diga ao Vue para usar o novo componente. Aqui est\u00e1 o c\u00f3digo final.<\/div>\n<div>\n<pre class=\"lang:js decode:true\">import config from '.\/config'\r\nimport Vue from 'vue'\r\nimport BootstrapVue from \"bootstrap-vue\"\r\nimport App from '.\/App.vue'\r\nimport \"bootstrap\/dist\/css\/bootstrap.min.css\"\r\nimport \"bootstrap-vue\/dist\/bootstrap-vue.css\"\r\nimport * as VueGoogleMaps from 'vue2-google-maps'\r\n\r\nVue.use(BootstrapVue)\r\n\r\nVue.use(VueGoogleMaps, {\r\n  load: {\r\n    key: config.googleMapsKey\r\n  }\r\n})\r\n\r\nnew Vue({\r\n  el: '#app',\r\n  render: h =&gt; h(App)\r\n})<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>Carregamos a chave da API do Google Maps de um arquivo <code>config.js<\/code>. Crie esse arquivo e, por enquanto, adicione este c\u00f3digo de espa\u00e7o reservado.<\/p>\n<pre class=\"lang:js decode:true\">export default {\r\n  googleMapsKey: ''\r\n}<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>Crie o projeto novamente (<code>npm run build<\/code>). Inicie o servidor, recarregue o site e voc\u00ea ver\u00e1 o in\u00edcio do nosso cliente real com esta apar\u00eancia.<\/p>\n<\/div>\n<div class=\"imageblock\">\n<div class=\"content\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/couchbaselabs\/blog-source-code\/master\/Greeley\/0048POI\/images\/RealClientBase.png\" alt=\"Initial Version of Tutorial Client\" \/><\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_the_web_server_code\">O c\u00f3digo do servidor web<\/h3>\n<div class=\"paragraph\">\n<p>Em seguida, preencheremos o lado do servidor. Nosso servidor alimenta as p\u00e1ginas da Web e exp\u00f5e a API REST de que precisamos. A API \u00e9, em sua maior parte, apenas um pacote de conveni\u00eancia em torno da funcionalidade do banco de dados.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Na fonte do servidor, substitua nosso <code>app.js<\/code> com isso.<\/p>\n<pre class=\"lang:js decode:true\">global.Promise = require('bluebird');\r\nrequire('dotenv').config();\r\n\r\nconst express = require('express');\r\n\r\nconst debug = require('debug')('poi:server');\r\nconst path = require('path');\r\nconst favicon = require('serve-favicon');\r\nconst logger = require('morgan');\r\nconst cookieParser = require('cookie-parser');\r\nconst bodyParser = require('body-parser');\r\nconst http = require('http');\r\nconst https = require('https');\r\nconst fs = require('fs');\r\n\r\nconst couchbase = require('couchbase');\r\nconst cluster = new couchbase.Cluster(process.env.CLUSTER);\r\ncluster.authenticate(process.env.CLUSTER_USER, process.env.CLUSTER_PASSWORD);\r\n\r\nconst app = express();\r\n\r\napp.locals.couchbase = couchbase;\r\napp.locals.cluster = cluster;\r\napp.locals.travel = cluster.openBucket('travel-sample');\r\napp.locals.eventing = cluster.openBucket('eventing');\r\n\r\napp.use(favicon(path.join(__dirname, 'images\/favicon.ico')));\r\n\r\napp.use(logger('dev'));\r\napp.use(bodyParser.json());\r\napp.use(bodyParser.urlencoded({ extended: false }));\r\napp.use(cookieParser());\r\n\r\napp.use(function(req, res, next) {\r\n  res.header(\"Access-Control-Allow-Origin\", \"*\");\r\n  res.header(\"Access-Control-Allow-Headers\", \"Origin, X-Requested-With, Content-Type, Accept\");\r\n  next();\r\n});\r\n\r\napp.use(express.static(path.join(__dirname, '..\/client')));\r\n\r\nconst records = require('.\/routes\/records');\r\napp.use('\/records', records);\r\nconst events = require('.\/routes\/events');\r\napp.use('\/events', events);\r\n\r\n\/\/ catch 404 and forward to error handler\r\napp.use(function(req, res, next) {\r\n  console.dir(req);\r\n  console.dir(res);\r\n  let err = new Error('Not Found');\r\n  err.status = 404;\r\n  next(err);\r\n});\r\n\r\n\/\/ error handler\r\napp.use(function(err, req, res, next) {\r\n  \/\/ set locals, only providing error in development\r\n  res.locals.message = err.message;\r\n  res.locals.error = req.app.get('env') === 'development' ? err : {};\r\n\r\n  \/\/ render the error page\r\n  res.status(err.status || 500);\r\n  res.render('error');\r\n});\r\n\r\n\/\/ HTTP server\r\n\r\nconst http_port = process.env.HTTP_PORT;\r\nconst http_server = http.createServer(app);\r\n\r\nhttp_server.listen(http_port);\r\n\r\nhttp_server.on('error', onError);\r\nhttp_server.on('listening', onListening);\r\n\r\n\/\/ HTTPS server\r\n\r\nconst options = {\r\n  key: fs.readFileSync(path.join('ssl', 'key.pem')),\r\n  cert: fs.readFileSync(path.join('ssl', 'cert.pem'))\r\n};\r\n\r\nconst https_port = process.env.HTTPS_PORT;\r\nconst https_server = https.createServer(options, app);\r\n\r\nhttps_server.listen(https_port);\r\n\r\nhttps_server.on('error', onError);\r\nhttps_server.on('listening', onListening);\r\n\r\n\/**\r\n * Event listener for HTTP\/S server \"error\" event.\r\n *\/\r\n\r\nfunction onError(error) {\r\n  if (error.syscall !== 'listen') {\r\n    throw error;\r\n  }\r\n\r\n  let bind = typeof port === 'string'\r\n    ? 'Pipe ' + port\r\n    : 'Port ' + port;\r\n\r\n  \/\/ handle specific listen errors with friendly messages\r\n  switch (error.code) {\r\n    case 'EACCES':\r\n      console.error(bind + ' requires elevated privileges');\r\n      process.exit(1);\r\n      break;\r\n    case 'EADDRINUSE':\r\n      console.error(bind + ' is already in use');\r\n      process.exit(1);\r\n      break;\r\n    default:\r\n      throw error;\r\n  }\r\n}\r\n\r\n\/**\r\n * Event listener for HTTP\/S server \"listening\" event.\r\n *\/\r\n\r\nfunction onListening() {\r\n  let addr = this.address();\r\n  let bind = typeof addr === 'string'\r\n    ? 'pipe ' + addr\r\n    : 'port ' + addr.port;\r\n  debug('Listening on ' + bind);\r\n}<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>As principais diferen\u00e7as s\u00e3o a configura\u00e7\u00e3o do cliente Node do Couchbase Server e a conex\u00e3o das rotas para os pontos de extremidade REST. H\u00e1 outros c\u00f3digos adicionais para coisas como servir em http e https tamb\u00e9m. N\u00e3o examinaremos essas partes.<\/p>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_connecting_to_couchbase_server\">Conex\u00e3o com o servidor Couchbase<\/h4>\n<div class=\"paragraph\">\n<p>Os dois blocos de c\u00f3digo para conex\u00e3o ao nosso banco de dados s\u00e3o muito simples.<\/p>\n<pre class=\"lang:js decode:true\">const couchbase = require('couchbase');\r\nconst cluster = new couchbase.Cluster(process.env.CLUSTER);\r\ncluster.authenticate(process.env.CLUSTER_USER, process.env.CLUSTER_PASSWORD);\r\n\r\n...\r\n\r\napp.locals.couchbase = couchbase;\r\napp.locals.cluster = cluster;\r\napp.locals.travel = cluster.openBucket('travel-sample');\r\napp.locals.eventing = cluster.openBucket('eventing');<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>As tr\u00eas primeiras linhas importam o cliente Node do Couchbase, criam um novo objeto de cluster que representa um cluster de n\u00f3s de banco de dados e autenticam esse cluster. Isso inicia a conex\u00e3o com o banco de dados.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Por conveni\u00eancia, adicionamos refer\u00eancias aos objetos do cliente e do cluster ao <code>app.locals<\/code>. Isso os torna dispon\u00edveis globalmente.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Por fim, o c\u00f3digo estabelece e salva conex\u00f5es com dois buckets. <a title=\"https:\/\/developer.couchbase.com\/documentation\/server\/current\/architecture\/core-data-access-buckets.html\" href=\"https:\/\/developer.couchbase.com\/documentation\/server\/current\/architecture\/core-data-access-buckets.html\">Baldes<\/a> s\u00e3o uma estrutura organizacional de alto n\u00edvel no Couchbase.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>O primeiro bucket ser\u00e1 preenchido com dados de amostra que v\u00eam com as instala\u00e7\u00f5es do Couchbase Server. Para o segundo bucket, estou fazendo algumas altera\u00e7\u00f5es. Precisamos de um bucket de metadados para o <a title=\"https:\/\/developer.couchbase.com\/documentation\/server\/5.5\/eventing\/eventing-overview.html\" href=\"https:\/\/developer.couchbase.com\/documentation\/server\/5.5\/eventing\/eventing-overview.html\">Servi\u00e7o de eventos<\/a>. Como veremos, precisamos de apenas alguns documentos extras armazenados, que precisam ir para algum lugar al\u00e9m do bucket principal. Em vez de criar um terceiro bucket, eu os coloco junto com os dados de eventos. Normalmente, voc\u00ea n\u00e3o usaria esse atalho na produ\u00e7\u00e3o.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_static_files_and_api_routes\">Arquivos est\u00e1ticos e rotas de API<\/h4>\n<div class=\"paragraph\">\n<p>Temos apenas algumas linhas de c\u00f3digo que precisamos direcionar ao Express para servir nossas p\u00e1ginas est\u00e1ticas criadas a partir do c\u00f3digo do cliente e para organizar nossa API de dados do servidor.<\/p>\n<pre class=\"lang:js decode:true\">app.use(express.static(path.join(__dirname, '..\/client')));\r\n\r\nconst records = require('.\/routes\/records');\r\napp.use('\/records', records);\r\nconst events = require('.\/routes\/events');\r\napp.use('\/events', events);<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>O modelo <code>index.html<\/code> A p\u00e1gina inicial do aplicativo adiciona <code>dist<\/code> para todos os caminhos de arquivos. Isso significa que nossos arquivos est\u00e1ticos s\u00e3o realmente servidos a partir de um diret\u00f3rio raiz de <code>\/cliente\/dist<\/code>.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Separei a API de dados em dois grupos, organizados em uma categoria geral <code>rotas<\/code> subdiret\u00f3rio. H\u00e1 os pontos de extremidade que come\u00e7am com <code>registros<\/code>. Eles recuperam dados do banco de dados.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>O <code>eventos<\/code> A rota \u00e9 exclusiva. Os pontos de extremidade s\u00e3o usados pelo cliente da Web e pelo Couchbase Eventing Service.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Vamos dar uma olhada no <code>registros<\/code> c\u00f3digo primeiro.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4>API de acesso ao banco de dados<\/h4>\n<pre class=\"lang:js decode:true\">const express = require('express');\r\nconst router = express.Router();\r\n\r\nrouter.get('\/destinations', async function(req, res, next) {\r\n  let couchbase = req.app.locals.couchbase;\r\n  let travel = req.app.locals.travel;\r\n  let queryPromise = Promise.promisify(travel.query, { context: travel });\r\n\r\n  let query = `SELECT DISTINCT airport.city as name\r\n              FROM \\`travel-sample\\` airport\r\n              INNER JOIN \\`travel-sample\\` hotel\r\n              USE HASH(probe)\r\n              ON hotel.city = airport.city\r\n              WHERE airport.type = 'airport'\r\n              AND hotel.type = 'hotel';`;\r\n\r\n  query = couchbase.N1qlQuery.fromString(query);\r\n\r\n  await queryPromise(query)\r\n  .then(rows =&gt; res.json(rows))\r\n  .catch(err =&gt; {\r\n    console.log(err);\r\n    res.status(500).send({ error: err });\r\n  });\r\n});\r\n\r\nrouter.get('\/hotels\/byCity\/:id', async function(req, res, next) {\r\n  let couchbase = req.app.locals.couchbase;\r\n  let travel = req.app.locals.travel;\r\n  let queryPromise = Promise.promisify(travel.query, { context: travel });\r\n\r\n  let query = `SELECT hotel.name, hotel.address, airport.airportname, airport.icao, hotel.geo\r\n              FROM \\`travel-sample\\` airport\r\n              INNER JOIN \\`travel-sample\\` hotel\r\n                ON hotel.type = 'hotel' AND hotel.city = airport.city\r\n              WHERE airport.type = 'airport'\r\n                AND airport.city = '${req.params.id}'\r\n              LIMIT 5;`;\r\n\r\n  query = couchbase.N1qlQuery.fromString(query);\r\n\r\n  await queryPromise(query)\r\n  .then(rows =&gt; res.json(rows))\r\n  .catch(err =&gt; {\r\n    console.log(err);\r\n    res.status(500).send({ error: err });\r\n  });\r\n});\r\n\r\nrouter.post('\/select\/geo', async function(req, res, next) {\r\n  let couchbase = req.app.locals.couchbase;\r\n  let travel = req.app.locals.travel;\r\n  let queryPromise = Promise.promisify(travel.query, { context: travel });\r\n\r\n  let location = JSON.stringify(req.body);\r\n\r\n  let query = `UPSERT INTO \\`travel-sample\\` (KEY, VALUE) VALUES('trigger', ${location})`;\r\n\r\n  query = couchbase.N1qlQuery.fromString(query);\r\n\r\n  await queryPromise(query)\r\n  .then(response =&gt; res.json(response))\r\n  .catch(err =&gt; {\r\n    console.log(err);\r\n    res.status(500).send({ error: err });\r\n  });\r\n});\r\n\r\nmodule.exports = router;<\/pre>\n<div class=\"paragraph\">\n<p>Temos tr\u00eas rotas definidas aqui, <code>\/destinos<\/code>, <code>\/hotels\/byCity\/:id<\/code>e <code>\/select\/geo<\/code>. Todos eles t\u00eam a mesma estrutura b\u00e1sica. Obtemos nossas refer\u00eancias de banco de dados, usamos o bluebird para criar vers\u00f5es de promessa do m\u00e9todo de consulta, constru\u00edmos um <a title=\"https:\/\/www.couchbase.com\/n1ql\" href=\"https:\/\/www.couchbase.com\/blog\/pt\/n1ql\/\">N1QL<\/a> disparar a consulta e retornar os resultados.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Vamos analisar as consultas, come\u00e7ando pela mais simples.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_n1ql_queries\">Consultas N1QL<\/h4>\n<div class=\"paragraph\">\n<p>Usamos o <code>\/select\/geo<\/code> para armazenar a escolha atual de hotel feita pelo usu\u00e1rio. Aqui est\u00e1 a consulta dividida.<\/p>\n<pre class=\"lang:default decode:true\">UPSERT INTO \\`travel-sample\\` (KEY, VALUE) VALUES('trigger', ${location})<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p><code>UPSERT<\/code> modificar\u00e1 um documento ou o criar\u00e1 se ele ainda n\u00e3o existir. Armazenamos a geolocaliza\u00e7\u00e3o do hotel escolhido em um documento com um id de <code>gatilho<\/code>. Isso provavelmente parece estranho. Isso far\u00e1 mais sentido mais tarde, quando chegarmos ao c\u00f3digo Eventing. O que realmente nos interessa n\u00e3o \u00e9 apenas a localiza\u00e7\u00e3o do hotel, mas os pontos de interesse pr\u00f3ximos. Esse documento acionar\u00e1 a sequ\u00eancia que recupera esses POIs. Da\u00ed a raz\u00e3o para chamar o documento <code>gatilho<\/code>.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Aqui est\u00e1 um exemplo do documento criado.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"title\">\n<p><strong>gatilho<\/strong><\/p>\n<pre class=\"lang:js decode:true\">{\r\n  \"accuracy\": \"APPROXIMATE\",\r\n  \"lat\": 43.9397954,\r\n  \"lon\": 4.805895400000054\r\n}<\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>Para entender o <code>\/hotels\/byCity\/:id<\/code> consulta, primeiro d\u00ea uma olhada em alguns documentos de exemplo.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"title\">\n<p><strong><strong>hotel_1359<\/strong><\/strong><\/p>\n<pre class=\"lang:js decode:true\">{\r\n  \"address\": \"13-15 Avenue Monclar\",\r\n  \"alias\": null,\r\n  \"checkin\": null,\r\n  \"checkout\": null,\r\n  \"city\": \"Avignon\",\r\n  \"country\": \"France\",\r\n  \"description\": \"Family run hotel overlooking a flowered garden, within a private carpark. Internet wi-fi available in the whole building. Recently renovated rooms with the typical Provencal style. 7 languages spoken. Private taxi service.\",\r\n  \"directions\": \"just behind the central station, which faces the main avenue of downtown and the bus station\",\r\n  \"email\": null,\r\n  \"fax\": \"04 26 23 68 31\",\r\n  \"free_breakfast\": true,\r\n  \"free_internet\": false,\r\n  \"free_parking\": true,\r\n  \"geo\": {\r\n    \"accuracy\": \"APPROXIMATE\",\r\n    \"lat\": 43.9397954,\r\n    \"lon\": 4.805895400000054\r\n  },\r\n  \"id\": 1359,\r\n  \"name\": \"Avignon Hotel Monclar\",\r\n  \"pets_ok\": true,\r\n  \"phone\": \"+33 4 90 86 20 14\",\r\n  \"price\": \"Double room with ensuite shower and bathroom \u20ac30-60, studios and apartments from \u20ac75, breakfast \u20ac7 can be taken in the garden in season 7:30AM 11AM\",\r\n  \"public_likes\": [\"Vicente Williamson\"],\r\n  \"reviews\": [...],\r\n  \"state\": \"Provence-Alpes-C\u00f4te d'Azur\",\r\n  \"title\": \"Avignon\",\r\n  \"tollfree\": null,\r\n  \"type\": \"hotel\",\r\n  \"url\": \"https:\/\/hotel-monclar.com\/en\",\r\n  \"vacancy\": true\r\n}<\/pre>\n<\/div>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"title\">\n<p><strong><strong>aeroporto_1361<\/strong><\/strong><\/p>\n<pre class=\"lang:default decode:true\">{\r\n  \"airportname\": \"Caumont\",\r\n  \"city\": \"Avignon\",\r\n  \"country\": \"France\",\r\n  \"faa\": \"AVN\",\r\n  \"geo\": {\r\n    \"alt\": 124,\r\n    \"lat\": 43.9073,\r\n    \"lon\": 4.901831\r\n  },\r\n  \"icao\": \"LFMV\",\r\n  \"id\": 1361,\r\n  \"type\": \"airport\",\r\n  \"tz\": \"Europe\/Paris\"\r\n}<\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>Para nossa tabela de hot\u00e9is, precisamos do nome do hotel, endere\u00e7o, geolocaliza\u00e7\u00e3o, nome do aeroporto e c\u00f3digo do aeroporto. Obviamente, isso \u00e9 uma combina\u00e7\u00e3o de dados de ambos os documentos. Fazemos isso usando um <code>INNER JOIN<\/code>. Aqui est\u00e1 a pergunta.<\/p>\n<pre class=\"lang:default decode:true\">SELECT hotel.name, hotel.address, airport.airportname, airport.icao, hotel.geo\r\nFROM `travel-sample` airport\r\nINNER JOIN `travel-sample` hotel\r\n  ON hotel.type = 'hotel' AND hotel.city = airport.city\r\nWHERE airport.type = 'airport'\r\n  AND airport.city = '${req.params.id}'\r\nLIMIT 5;<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>Ao analis\u00e1-lo, voc\u00ea pode ver que conseguimos realizar a uni\u00e3o usando documentos do mesmo bucket. Eu uso aliases para deixar as coisas mais claras. Usamos a cidade de cada documento para formar a condi\u00e7\u00e3o de uni\u00e3o. Observe que tamb\u00e9m uso o documento <code>tipo<\/code>tanto na condi\u00e7\u00e3o de uni\u00e3o quanto na condi\u00e7\u00e3o de <code>ONDE<\/code> cl\u00e1usula. As condi\u00e7\u00f5es de uni\u00e3o podem ser bastante sofisticadas. Leia isto <a title=\"https:\/\/www.couchbase.com\/blog\/ansi-join-support-n1ql\/\" href=\"https:\/\/www.couchbase.com\/blog\/pt\/ansi-join-support-n1ql\/\">blog<\/a> para obter mais detalhes e exemplos.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Por fim, vamos examinar como chegamos \u00e0 nossa lista de cidades em primeiro lugar. Esta \u00e9 a consulta para o <code>\/destinos<\/code>ponto final.<\/p>\n<pre class=\"lang:default decode:true\">SELECT DISTINCT airport.city as name\r\nFROM `travel-sample` airport\r\nINNER JOIN `travel-sample` hotel\r\nUSE HASH(probe)\r\n  ON hotel.city = airport.city\r\nWHERE airport.type = 'airport'\r\n  AND hotel.type = 'hotel';<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>O \u00fanico resultado retornado \u00e9 uma lista de nomes de cidades. Nesse caso, estamos usando uma jun\u00e7\u00e3o interna efetivamente como um filtro. Ao fazer a correspond\u00eancia entre cidades aeroportu\u00e1rias e cidades hoteleiras, obtemos uma lista apenas das cidades que t\u00eam ambas.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>As uni\u00f5es internas podem usar duas abordagens diferentes em termos de algoritmo. A primeira uni\u00e3o que examinamos usa a uni\u00e3o de loop aninhado padr\u00e3o.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Este \u00faltimo exemplo usa uma tabela de hash na mem\u00f3ria. Isso pode acelerar significativamente uma uni\u00e3o, especialmente quando um dos dois conjuntos de dados \u00e9 pequeno. Usamos a tabela <a title=\"https:\/\/developer.couchbase.com\/documentation\/server\/current\/n1ql\/n1ql-language-reference\/from.html#story-h2-10\" href=\"https:\/\/developer.couchbase.com\/documentation\/server\/current\/n1ql\/n1ql-language-reference\/from.html#story-h2-10\">\"USE HASH()\"<\/a> para informar ao N1QL como queremos que a consulta seja otimizada. H\u00e1 um lado \"probe\" e um lado \"build\". A tabela de hash \u00e9 criada a partir dos dados do lado da constru\u00e7\u00e3o. A jun\u00e7\u00e3o \u00e9 realizada fazendo pesquisas a partir dos dados do lado da sonda.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>A dica que demos acima diz ao N1QL para usar os dados do hotel para o lado da sonda nesse caso. Ou seja, ele criar\u00e1 a tabela a partir dos dados do aeroporto e, em seguida, far\u00e1 as pesquisas de hash usando os dados do hotel.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Se ainda n\u00e3o o fez, recomendo que experimente essas consultas diretamente no Couchbase Server Query Workbench, parte do console de administra\u00e7\u00e3o da Web.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_server_sent_events\">Eventos enviados pelo servidor<\/h4>\n<div class=\"paragraph\">\n<p>J\u00e1 mencionamos a configura\u00e7\u00e3o de um ouvinte de eventos para <a title=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Server-sent_events\/Using_server-sent_events\" href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/API\/Server-sent_events\/Using_server-sent_events\">eventos enviados pelo servidor<\/a> no lado do cliente. Esses dois pontos de extremidade mostram o que \u00e9 necess\u00e1rio no servidor.<\/p>\n<pre class=\"lang:js decode:true\">const express = require('express');\r\nconst router = express.Router();\r\nconst sse = require('sse-channel');\r\n\r\nconst poi = new sse();\r\n\r\nrouter.get('\/poi', (req, res) =&gt; poi.addClient(req, res));\r\n\r\nrouter.post('\/poi', async function(req, res, next) {\r\n  res.send('');\r\n\r\n  let msg = { event: 'poi' };\r\n\r\n  msg.data = JSON.stringify(req.body);\r\n\r\n  poi.send(msg);\r\n});\r\n\r\nmodule.exports = router;<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>A vers\u00e3o \"get\" do <code>poi<\/code> \u00e9 chamado pelo navegador quando o ponto de extremidade <code>Origem do evento<\/code> \u00e9 constru\u00eddo. Voc\u00ea pode ver que simplesmente adicionamos o chamador como um cliente.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Usamos a vers\u00e3o \"post\" como intermedi\u00e1ria para enviar dados ao cliente. A vers\u00e3o <code>res.send('');<\/code> d\u00e1 uma dica de como isso funciona. No c\u00f3digo Eventing, usaremos os recursos cURL do N1QL para enviar dados para esse endpoint. A resposta vazia est\u00e1 l\u00e1 para encerrar essa transa\u00e7\u00e3o.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Em seguida, o servidor encaminha os dados para todos os clientes que estiverem ouvindo. H\u00e1 muito mais detalhes. Se voc\u00ea quiser saber mais, <a title=\"https:\/\/www.html5rocks.com\/en\/tutorials\/eventsource\/basics\/\" href=\"https:\/\/www.html5rocks.com\/en\/tutorials\/eventsource\/basics\/\">este artigo<\/a> tem boas informa\u00e7\u00f5es.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_finishing_the_server\">Finaliza\u00e7\u00e3o do servidor<\/h4>\n<div class=\"paragraph\">\n<p>Para concluir o lado do servidor do nosso projeto, crie um subdiret\u00f3rio <code>rotas<\/code> no diret\u00f3rio do servidor.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Copiar o <code>registros<\/code> acima em um arquivo em rotas chamado <code>registros.js<\/code>. Copiar o <code>eventos<\/code> c\u00f3digo acima em um arquivo chamado <code>eventos.js<\/code>. E, finalmente, no pr\u00f3prio diret\u00f3rio do servidor, crie um novo arquivo chamado <code>.env<\/code>. Cole os seguintes par\u00e2metros de configura\u00e7\u00e3o e salve. (\u00c9 claro que voc\u00ea pode alterar as configura\u00e7\u00f5es conforme necess\u00e1rio).<\/p>\n<pre class=\"lang:default decode:true\">HTTP_PORT=8080\r\nHTTPS_PORT=8081\r\nDEBUG=node,http,poi:*\r\nCLUSTER='couchbase:\/\/localhost:8091'\r\nCLUSTER_USER=Administrator\r\nCLUSTER_PASSWORD=password<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>O servidor deve estar pronto para funcionar agora. No diret\u00f3rio raiz do servidor, execute <code>n\u00f3 app.js<\/code>. N\u00e3o se esque\u00e7a de criar o c\u00f3digo do cliente primeiro.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_the_couchbase_server_eventing_service_code\">C\u00f3digo do servi\u00e7o de eventos do Couchbase Server<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Aqui est\u00e1 a \u00faltima parte do molho especial que faz esse aplicativo funcionar. Na vers\u00e3o 5.5.0, o Couchbase introduziu a fun\u00e7\u00e3o <a title=\"https:\/\/www.couchbase.com\/blog\/eventing\/\" href=\"https:\/\/www.couchbase.com\/blog\/pt\/eventing\/\">Servi\u00e7o de eventos<\/a>. Esse \u00e9 provavelmente o meu novo recurso favorito na s\u00e9rie de vers\u00f5es 5.5. O Couchbase Functions \u00e9 o primeiro componente oferecido como parte desse servi\u00e7o. Em resumo, o Functions permite que voc\u00ea execute c\u00f3digo <strong>no servidor de banco de dados<\/strong> em resposta a altera\u00e7\u00f5es no banco de dados.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>As fun\u00e7\u00f5es s\u00e3o escritas em JavaScript padr\u00e3o, com algumas <a title=\"https:\/\/developer.couchbase.com\/documentation\/server\/current\/eventing\/eventing-language-constructs.html\" href=\"https:\/\/developer.couchbase.com\/documentation\/server\/current\/eventing\/eventing-language-constructs.html\">acr\u00e9scimos e restri\u00e7\u00f5es<\/a>. Para criar a fun\u00e7\u00e3o de que precisamos, siga estas etapas.<\/p>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_eventing_meta_data_bucket\">Balde de metadados de eventos<\/h3>\n<div class=\"paragraph\">\n<p>Primeiro, crie um compartimento para os metadados de eventos.<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>Abra o console do Couchbase Server e fa\u00e7a login, se necess\u00e1rio<\/li>\n<li>Clique em \"Buckets\" (Compartimentos) no menu do lado esquerdo<\/li>\n<li>Clique em \"Add Bucket\" no canto superior direito<\/li>\n<li>Entrar <code>eventos<\/code> para o nome do balde na caixa de di\u00e1logo que \u00e9 exibida<\/li>\n<li>Clique em \"Add Bucket\" para concluir<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_adding_a_function\">Adi\u00e7\u00e3o de uma fun\u00e7\u00e3o<\/h3>\n<div class=\"paragraph\">\n<p>Agora, configure a fun\u00e7\u00e3o e adicione o c\u00f3digo.<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>Clique em \"Eventing\" no menu do lado esquerdo<\/li>\n<li>Clique em \"Add Function\" (Adicionar fun\u00e7\u00e3o) no canto superior direito<\/li>\n<\/ul>\n<\/div>\n<div class=\"paragraph\">\n<p>Isso abrir\u00e1 uma caixa de di\u00e1logo.<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>Selecione <code>amostra de viagem<\/code> como o Bucket de origem<\/li>\n<li>Selecione <code>eventos<\/code> como o Bucket de Metadados<\/li>\n<li>Entrar <code>monitor<\/code> (ou o que voc\u00ea quiser) para o nome da fun\u00e7\u00e3o<\/li>\n<li>Em \"Bindings\", defina <code>tipo<\/code> para \"Alias\", <code>nome<\/code> para \"travel-sample\" e <code>valor<\/code> para \"db\"<\/li>\n<li>Clique em \"Next: Adicionar c\u00f3digo\"<\/li>\n<\/ul>\n<p><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/couchbaselabs\/blog-source-code\/master\/Greeley\/0048POI\/images\/POI_function.png\" alt=\"Adding a Function\" \/><\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Isso o levar\u00e1 ao editor de c\u00f3digo. Ele \u00e9 pr\u00e9-preenchido com as assinaturas de fun\u00e7\u00e3o. Em vez disso, copie este c\u00f3digo.<\/p>\n<\/div>\n<\/div>\n<p>https:\/\/gist.github.com\/HodGreeley\/9e25f9072247e180ec5cd764d9048c3b#file-poi-js<\/p>\n<div class=\"sect2\">\n<h3 id=\"_deploying_a_function\">Implementa\u00e7\u00e3o de uma fun\u00e7\u00e3o<\/h3>\n<div class=\"paragraph\">\n<p>Para implementar esse c\u00f3digo, primeiro clique em \"Save\" (Salvar) e, em seguida, clique novamente em \"Eventing\" (Eventos) no menu do lado esquerdo. Voc\u00ea dever\u00e1 ver uma entrada para a fun\u00e7\u00e3o. Clique em qualquer lugar dessa barra. Voc\u00ea ver\u00e1 que ela se expande.<\/p>\n<\/div>\n<div class=\"imageblock\">\n<div class=\"content\"><img decoding=\"async\" src=\"https:\/\/raw.githubusercontent.com\/couchbaselabs\/blog-source-code\/master\/Greeley\/0048POI\/images\/DeployFunction.png\" alt=\"Deploying a Function\" \/><\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>Clique em \"Deploy\" e, em seguida, clique em \"Deploy Function\".<\/p>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_understanding_the_function_code\">Entendendo o c\u00f3digo de fun\u00e7\u00e3o<\/h3>\n<div class=\"paragraph\">\n<p><code>Sobre a atualiza\u00e7\u00e3o<\/code> \u00e9 chamado sempre que um documento \u00e9 alterado. Ele recebe o documento e os metadados do documento como par\u00e2metros.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Estamos procurando o <code>gatilho<\/code> documento a ser alterado, indicando a sele\u00e7\u00e3o de um novo hotel. A primeira linha filtra todos os outros documentos com base no ID do documento (\u00e0s vezes chamado de chave do documento).<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>A pr\u00f3xima linha mostra alguns aspectos interessantes. Lembre-se <code>db<\/code> \u00e9 um alias para o bucket de amostras de viagem. <code>db['here']<\/code> recupera diretamente um documento com id <code>aqui<\/code>. \u00c9 aqui que armazenaremos as credenciais necess\u00e1rias para o <a title=\"https:\/\/www.here.com\/\" href=\"https:\/\/www.here.com\/\">AQUI<\/a> servi\u00e7os de mapeamento.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Preparamos a URL e os dados para nossa solicita\u00e7\u00e3o de pontos de interesse. O Here tem muitos recursos interessantes em sua API. Estamos apenas fazendo uma solicita\u00e7\u00e3o b\u00e1sica.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Com essas informa\u00e7\u00f5es em m\u00e3os, estamos prontos para nossa chamada cURL. Ao criar a consulta N1QL, vemos uma das modifica\u00e7\u00f5es no JavaScript padr\u00e3o: Voc\u00ea pode escrever suas consultas em linha da mesma forma que as construiria no Query Workbench.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Vemos outro detalhe interessante na consulta cURL. O N1QL oferece uma sintaxe conveniente para filtrar resultados. Ao adicionar o caminho <code>.resultados.item<\/code> at\u00e9 o final, obtemos apenas os dados que desejamos.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Em seguida, executamos a consulta e, usando o mesmo <code>db[]<\/code> abreviado, atualize nosso <code>poi<\/code> documento. Esse \u00e9 um exemplo de uso de uma fun\u00e7\u00e3o para aumentar os dados. Em outro cen\u00e1rio, podemos derivar nossa atualiza\u00e7\u00e3o inteiramente de registros no banco de dados. Por exemplo, voc\u00ea pode preencher todos os detalhes de um carrinho de compras \u00e0 medida que um cliente faz sele\u00e7\u00f5es.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Por fim, com nossos pontos de interesse em m\u00e3os, usamos novamente o cURL para enviar os dados ao endpoint do nosso servidor Web. Lembre-se da vers\u00e3o \"post\" da fun\u00e7\u00e3o <code>poi<\/code> A API ingere os dados recebidos e os envia de volta a todos os clientes registrados. Assim, podemos fazer com que a interface do usu\u00e1rio do cliente reaja \u00e0s altera\u00e7\u00f5es no banco de dados sem precisar fazer pesquisas.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_final_steps\">Etapas finais<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Agora estamos prontos para juntar tudo isso. Voc\u00ea pode experimentar o aplicativo como est\u00e1, mas a parte de mapas ainda n\u00e3o funcionar\u00e1. Para isso, voc\u00ea precisa de uma chave da API do Google Maps e de um conjunto de credenciais da HERE.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>A chave do Maps vai para o <code>config.js<\/code> no c\u00f3digo do cliente. Salve as chaves HERE em um documento na pasta <code>eventos<\/code> balde. Voc\u00ea pode fazer isso diretamente no console de administra\u00e7\u00e3o clicando em \"Documents\" (Documentos) no menu \u00e0 esquerda e, em seguida, em \"Add Document\" (Adicionar documento) no canto superior direito. Use isso como um modelo.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"title\">\n<p><strong><strong>aqui<\/strong><\/strong><\/p>\n<pre class=\"lang:js decode:true\">{\r\n  \"id\": \"TPxxxxxxxxxxxxxxxxxx\",\r\n  \"code\": \"whsxxxxxxxxxxxxxxxxxxx\"\r\n}<\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>E, por \u00faltimo, como medida de seguran\u00e7a, o cURL \u00e9 desativado por padr\u00e3o. No console de administra\u00e7\u00e3o, fa\u00e7a o seguinte.<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>Clique em \"Settings\" (Configura\u00e7\u00f5es) no menu do lado esquerdo<\/li>\n<li>Clique para expandir \"Configura\u00e7\u00f5es avan\u00e7adas de consulta\"<\/li>\n<li>Selecione \"Unrestricted\" (Irrestrito) em \"CURL() Function Access\" (Acesso \u00e0 fun\u00e7\u00e3o CURL())<\/li>\n<\/ul>\n<\/div>\n<div class=\"paragraph\">\n<p>Isso \u00e9 <strong>n\u00e3o<\/strong> o que voc\u00ea deseja para a produ\u00e7\u00e3o. Em vez disso, voc\u00ea desejaria uma lista branca de um conjunto selecionado de URLs. No entanto, isso ser\u00e1 suficiente para o nosso projeto.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Com isso, no diret\u00f3rio do servidor da Web, execute <code>n\u00f3 app.js<\/code>. Abrir <code>localhost:8080<\/code> em seu navegador (ou o que voc\u00ea escolheu em <code>.env<\/code>) e experiment\u00e1-lo.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_source\">Fonte<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Voc\u00ea pode encontrar o c\u00f3digo-fonte de todo o aplicativo no GitHub <a title=\"https:\/\/github.com\/couchbaselabs\/points-of-interest\" href=\"https:\/\/github.com\/couchbaselabs\/points-of-interest\">aqui<\/a>. Inclu\u00ed um script <code>configura\u00e7\u00e3o<\/code> para simplificar a prepara\u00e7\u00e3o de tudo. Basta executar <code>.\/setup<\/code> e forne\u00e7a suas chaves. (Talvez seja necess\u00e1rio torn\u00e1-lo execut\u00e1vel primeiro.) Voc\u00ea ainda precisa executar <code>npm install<\/code>e criar o c\u00f3digo do cliente.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_webinar\">Webinar<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Este aplicativo foi usado como parte de uma demonstra\u00e7\u00e3o em um webinar do Couchbase. Voc\u00ea pode ver uma grava\u00e7\u00e3o dele <a title=\"https:\/\/event.on24.com\/wcc\/r\/1774871\/5A84BC9DF67C6113F8DD95AD7DF71BBA\" href=\"https:\/\/event.on24.com\/wcc\/r\/1774871\/5A84BC9DF67C6113F8DD95AD7DF71BBA\">aqui<\/a>. N\u00e3o deixe de conferir outros <a title=\"https:\/\/www.couchbase.com\/resources\/webinars\" href=\"https:\/\/www.couchbase.com\/blog\/pt\/resources\/webinars\/\">webinars<\/a> na \u00e1rea de recursos do Couchbase.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_postscript\">P\u00f3s-escrito<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>O Couchbase \u00e9 de c\u00f3digo aberto e <a title=\"https:\/\/www.couchbase.com\/downloads\" href=\"https:\/\/www.couchbase.com\/blog\/pt\/downloads\/\">gr\u00e1tis para experimentar<\/a>.<br \/>\n<strong>Comece a usar<\/strong> com <a title=\"https:\/\/www.couchbase.com\/get-started\" href=\"https:\/\/developer.couchbase.com\/tutorials\">c\u00f3digo de amostra, consultas de exemplo, tutoriais e muito mais<\/a>.<br \/>\nEncontre mais recursos em nosso <a title=\"https:\/\/developer.couchbase.com\/community\" href=\"https:\/\/developer.couchbase.com\/community\">portal do desenvolvedor<\/a>.<br \/>\nSiga-nos no Twitter <a title=\"https:\/\/twitter.com\/CouchbaseDev\" href=\"https:\/\/twitter.com\/CouchbaseDev\">@CouchbaseDev<\/a>.<br \/>\nVoc\u00ea pode postar perguntas em nosso <a title=\"https:\/\/www.couchbase.com\/forums\/\" href=\"https:\/\/www.couchbase.com\/blog\/pt\/forums\/\">f\u00f3runs<\/a>.<br \/>\nParticipamos ativamente de <a title=\"https:\/\/stackoverflow.com\/questions\/tagged\/couchbase\" href=\"https:\/\/stackoverflow.com\/questions\/tagged\/couchbase\">Estouro de pilha<\/a>.<br \/>\nEntre em contato comigo pelo Twitter com perguntas, coment\u00e1rios, t\u00f3picos que voc\u00ea gostaria de ver etc. <a title=\"https:\/\/twitter.com\/HodGreeley\" href=\"https:\/\/twitter.com\/HodGreeley\">@HodGreeley<\/a><\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>","protected":false},"excerpt":{"rendered":"<p>Introduction In this tutorial, we\u2019ll build a full stack application using Vue.js, Node.js, Express, and Couchbase Server. In addition to those frameworks, we\u2019ll use the Google Maps and Here Places REST APIs. Tl;dr clone the repo here. Contents What We\u2019ll [&hellip;]<\/p>","protected":false},"author":73,"featured_media":5541,"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":[1254,1588,1500,2083],"ppma_author":[9042],"class_list":["post-5538","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-express","tag-full-stack","tag-tutorial","tag-vue-js"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.9 (Yoast SEO v25.9) - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Build an App with Vue.js, Node.js, Express &amp; Couchbase<\/title>\n<meta name=\"description\" content=\"Map points of interest with this full stack tutorial using Vue.js, Node.js, Express, and 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\/pt\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/\" \/>\n<meta property=\"og:locale\" content=\"pt_BR\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Tutorial: Build a Points of Interest App with Vue.js, Node.js, Express, and Couchbase Server\" \/>\n<meta property=\"og:description\" content=\"Map points of interest with this full stack tutorial using Vue.js, Node.js, Express, and Couchbase.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.couchbase.com\/blog\/pt\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/\" \/>\n<meta property=\"og:site_name\" content=\"The Couchbase Blog\" \/>\n<meta property=\"article:published_time\" content=\"2018-07-26T22:42:30+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-06-14T04:23:10+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/F_of_CB.eq_.POI_.gif\" \/>\n\t<meta property=\"og:image:width\" content=\"960\" \/>\n\t<meta property=\"og:image:height\" content=\"540\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/gif\" \/>\n<meta name=\"author\" content=\"Hod Greeley, Developer Advocate, Couchbase\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@HodGreeley\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Hod Greeley, Developer Advocate, Couchbase\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"16 minutos\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/\"},\"author\":{\"name\":\"Hod Greeley, Developer Advocate, Couchbase\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/9b62593c8a13531e53d52fcd5aabbca4\"},\"headline\":\"Tutorial: Build a Points of Interest App with Vue.js, Node.js, Express, and Couchbase Server\",\"datePublished\":\"2018-07-26T22:42:30+00:00\",\"dateModified\":\"2025-06-14T04:23:10+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/\"},\"wordCount\":3329,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/F_of_CB.eq_.POI_.gif\",\"keywords\":[\"express\",\"full stack\",\"tutorial\",\"vue.js\"],\"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\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/\",\"name\":\"Build an App with Vue.js, Node.js, Express & Couchbase\",\"isPartOf\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/F_of_CB.eq_.POI_.gif\",\"datePublished\":\"2018-07-26T22:42:30+00:00\",\"dateModified\":\"2025-06-14T04:23:10+00:00\",\"description\":\"Map points of interest with this full stack tutorial using Vue.js, Node.js, Express, and Couchbase.\",\"breadcrumb\":{\"@id\":\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#breadcrumb\"},\"inLanguage\":\"pt-BR\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#primaryimage\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/F_of_CB.eq_.POI_.gif\",\"contentUrl\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/F_of_CB.eq_.POI_.gif\",\"width\":960,\"height\":540},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.couchbase.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Tutorial: Build a Points of Interest App with Vue.js, Node.js, Express, and Couchbase Server\"}]},{\"@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\/9b62593c8a13531e53d52fcd5aabbca4\",\"name\":\"Hod Greeley, Developer Advocate, Couchbase\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"pt-BR\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/21eb69cb5d4a401fb23b149e4f4e9e87\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/52d0018695c0ced0d1c68cf64a6195c81dbac03dce5983f98eb209e7c84350df?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/52d0018695c0ced0d1c68cf64a6195c81dbac03dce5983f98eb209e7c84350df?s=96&d=mm&r=g\",\"caption\":\"Hod Greeley, Developer Advocate, Couchbase\"},\"description\":\"Hod Greeley is a Developer Advocate for Couchbase, living in Silicon Valley. He has over two decades of experience as a software engineer and engineering manager. He has worked in a variety of software fields, including computational physics and chemistry, computer and network security, finance, and mobile. Prior to joining Couchbase in 2016, Hod led developer relations for mobile at Samsung. Hod holds a Ph.D. in chemical physics from Columbia University.\",\"sameAs\":[\"https:\/\/hod.greeley.org\/blog\",\"https:\/\/x.com\/HodGreeley\"],\"url\":\"https:\/\/www.couchbase.com\/blog\/pt\/author\/hod-greeley\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Build an App with Vue.js, Node.js, Express & Couchbase","description":"Mapeie pontos de interesse com este tutorial de pilha completa usando Vue.js, Node.js, Express e 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\/pt\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/","og_locale":"pt_BR","og_type":"article","og_title":"Tutorial: Build a Points of Interest App with Vue.js, Node.js, Express, and Couchbase Server","og_description":"Map points of interest with this full stack tutorial using Vue.js, Node.js, Express, and Couchbase.","og_url":"https:\/\/www.couchbase.com\/blog\/pt\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/","og_site_name":"The Couchbase Blog","article_published_time":"2018-07-26T22:42:30+00:00","article_modified_time":"2025-06-14T04:23:10+00:00","og_image":[{"width":960,"height":540,"url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/F_of_CB.eq_.POI_.gif","type":"image\/gif"}],"author":"Hod Greeley, Developer Advocate, Couchbase","twitter_card":"summary_large_image","twitter_creator":"@HodGreeley","twitter_misc":{"Written by":"Hod Greeley, Developer Advocate, Couchbase","Est. reading time":"16 minutos"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#article","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/"},"author":{"name":"Hod Greeley, Developer Advocate, Couchbase","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/9b62593c8a13531e53d52fcd5aabbca4"},"headline":"Tutorial: Build a Points of Interest App with Vue.js, Node.js, Express, and Couchbase Server","datePublished":"2018-07-26T22:42:30+00:00","dateModified":"2025-06-14T04:23:10+00:00","mainEntityOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/"},"wordCount":3329,"commentCount":1,"publisher":{"@id":"https:\/\/www.couchbase.com\/blog\/#organization"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/F_of_CB.eq_.POI_.gif","keywords":["express","full stack","tutorial","vue.js"],"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\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/","url":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/","name":"Build an App with Vue.js, Node.js, Express & Couchbase","isPartOf":{"@id":"https:\/\/www.couchbase.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#primaryimage"},"image":{"@id":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#primaryimage"},"thumbnailUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/F_of_CB.eq_.POI_.gif","datePublished":"2018-07-26T22:42:30+00:00","dateModified":"2025-06-14T04:23:10+00:00","description":"Mapeie pontos de interesse com este tutorial de pilha completa usando Vue.js, Node.js, Express e Couchbase.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#breadcrumb"},"inLanguage":"pt-BR","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/"]}]},{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#primaryimage","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/F_of_CB.eq_.POI_.gif","contentUrl":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/sites\/1\/2018\/07\/F_of_CB.eq_.POI_.gif","width":960,"height":540},{"@type":"BreadcrumbList","@id":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.couchbase.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Tutorial: Build a Points of Interest App with Vue.js, Node.js, Express, and Couchbase Server"}]},{"@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\/9b62593c8a13531e53d52fcd5aabbca4","name":"Hod Greeley, Advogado do desenvolvedor, Couchbase","image":{"@type":"ImageObject","inLanguage":"pt-BR","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/person\/image\/21eb69cb5d4a401fb23b149e4f4e9e87","url":"https:\/\/secure.gravatar.com\/avatar\/52d0018695c0ced0d1c68cf64a6195c81dbac03dce5983f98eb209e7c84350df?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/52d0018695c0ced0d1c68cf64a6195c81dbac03dce5983f98eb209e7c84350df?s=96&d=mm&r=g","caption":"Hod Greeley, Developer Advocate, Couchbase"},"description":"Hod Greeley \u00e9 um defensor dos desenvolvedores da Couchbase e mora no Vale do Sil\u00edcio. Ele tem mais de duas d\u00e9cadas de experi\u00eancia como engenheiro de software e gerente de engenharia. Trabalhou em diversas \u00e1reas de software, incluindo f\u00edsica e qu\u00edmica computacional, seguran\u00e7a de computadores e redes, finan\u00e7as e dispositivos m\u00f3veis. Antes de ingressar na Couchbase em 2016, Hod liderou as rela\u00e7\u00f5es com desenvolvedores para dispositivos m\u00f3veis na Samsung. Hod \u00e9 Ph.D. em f\u00edsica qu\u00edmica pela Universidade de Columbia.","sameAs":["https:\/\/hod.greeley.org\/blog","https:\/\/x.com\/HodGreeley"],"url":"https:\/\/www.couchbase.com\/blog\/pt\/author\/hod-greeley\/"}]}},"authors":[{"term_id":9042,"user_id":73,"is_guest":0,"slug":"hod-greeley","display_name":"Hod Greeley, Developer Advocate, Couchbase","avatar_url":"https:\/\/secure.gravatar.com\/avatar\/52d0018695c0ced0d1c68cf64a6195c81dbac03dce5983f98eb209e7c84350df?s=96&d=mm&r=g","author_category":"","last_name":"Greeley","first_name":"Hod","job_title":"","user_url":"https:\/\/hod.greeley.org\/blog","description":"Hod Greeley \u00e9 um defensor dos desenvolvedores da Couchbase e mora no Vale do Sil\u00edcio. Ele tem mais de duas d\u00e9cadas de experi\u00eancia como engenheiro de software e gerente de engenharia. Trabalhou em diversas \u00e1reas de software, incluindo f\u00edsica e qu\u00edmica computacional, seguran\u00e7a de computadores e redes, finan\u00e7as e dispositivos m\u00f3veis. Antes de ingressar na Couchbase em 2016, Hod liderou as rela\u00e7\u00f5es com desenvolvedores para dispositivos m\u00f3veis na Samsung. Hod \u00e9 Ph.D. em f\u00edsica qu\u00edmica pela Universidade de Columbia."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/5538","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\/73"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/comments?post=5538"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/posts\/5538\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media\/5541"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/media?parent=5538"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/categories?post=5538"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/tags?post=5538"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/pt\/wp-json\/wp\/v2\/ppma_author?post=5538"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}