{"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\/es\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/","title":{"rendered":"Tutorial: Crear una aplicaci\u00f3n de puntos de inter\u00e9s con Vue.js, Node.js, Express y Couchbase Server"},"content":{"rendered":"<p><span class=\"image\"><a class=\"image\" title=\"https:\/\/www.couchbase.com\/downloads\" href=\"https:\/\/www.couchbase.com\/blog\/es\/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\">Introducci\u00f3n<\/h2>\n<div class=\"paragraph\">\n<p>En este tutorial, crearemos una aplicaci\u00f3n de pila completa utilizando <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\">Express<\/a>y <a title=\"https:\/\/www.couchbase.com\/\" href=\"https:\/\/www.couchbase.com\/blog\/es\/\">Servidor Couchbase<\/a>. Adem\u00e1s de estos frameworks, utilizaremos el framework <a title=\"https:\/\/developers.google.com\/maps\/documentation\/\" href=\"https:\/\/developers.google.com\/maps\/documentation\/\">Google Maps<\/a> y <a title=\"https:\/\/developer.here.com\/develop\/rest-apis\" href=\"https:\/\/developer.here.com\/develop\/rest-apis\">Aqu\u00ed Lugares<\/a> API REST.<\/p>\n<\/div>\n<div id=\"toc\" class=\"toc\">\n<div id=\"toctitle\" class=\"title\">\n<div class=\"paragraph\">\n<p><strong>Tl;dr<\/strong> clonar el repositorio <a title=\"https:\/\/github.com\/couchbaselabs\/points-of-interest:\" href=\"https:\/\/github.com\/couchbaselabs\/points-of-interest:\">aqu\u00ed<\/a>.<\/p>\n<\/div>\n<p>Contenido<\/p>\n<\/div>\n<ul class=\"sectlevel1\">\n<li><a title=\"\" href=\"#_what_well_build\">Qu\u00e9 construiremos<\/a><\/li>\n<li><a title=\"\" href=\"#_what_you_need\">Lo que necesita<\/a><\/li>\n<li><a title=\"\" href=\"#_getting_started\">Primeros pasos<\/a><\/li>\n<li><a title=\"\" href=\"#_the_web_client_skeleton\">El esqueleto del cliente web<\/a>\n<ul class=\"sectlevel2\">\n<li><a title=\"\" href=\"#_generating_the_vue_js_client_scaffolding\">Generaci\u00f3n del andamiaje de cliente Vue.js<\/a><\/li>\n<li><a title=\"\" href=\"#_restructuring_and_fixup\">Reestructuraci\u00f3n y arreglo<\/a><\/li>\n<li><a title=\"\" href=\"#_install_dependencies_and_build\">Instalar dependencias y compilar<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a title=\"\" href=\"#_the_web_server_skeleton\">El esqueleto del servidor web<\/a><\/li>\n<li><a title=\"\" href=\"#_fleshing_out_the_client_and_server\">El cliente y el servidor<\/a>\n<ul class=\"sectlevel2\">\n<li><a title=\"\" href=\"#_the_web_client_code\">El c\u00f3digo del cliente web<\/a><\/li>\n<li><a title=\"\" href=\"#_the_web_server_code\">El c\u00f3digo del servidor web<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a title=\"\" href=\"#_the_couchbase_server_eventing_service_code\">El c\u00f3digo del servicio de eventos del servidor Couchbase<\/a>\n<ul class=\"sectlevel2\">\n<li><a title=\"\" href=\"#_eventing_meta_data_bucket\">Cubo de metadatos de eventos<\/a><\/li>\n<li><a title=\"\" href=\"#_adding_a_function\">A\u00f1adir una funci\u00f3n<\/a><\/li>\n<li><a title=\"\" href=\"#_deploying_a_function\">Despliegue de una funci\u00f3n<\/a><\/li>\n<li><a title=\"\" href=\"#_understanding_the_function_code\">Comprender el c\u00f3digo de funci\u00f3n<\/a><\/li>\n<\/ul>\n<\/li>\n<li><a title=\"\" href=\"#_final_steps\">Pasos finales<\/a><\/li>\n<li><a title=\"\" href=\"#_source\">Fuente<\/a><\/li>\n<li><a title=\"\" href=\"#_webinar\">Webinar<\/a><\/li>\n<li><a title=\"\" href=\"#_postscript\">Posdata<\/a><\/li>\n<\/ul>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_what_well_build\">Qu\u00e9 construiremos<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Vamos a crear una aplicaci\u00f3n web de una sola p\u00e1gina que muestre puntos de inter\u00e9s (POI) alrededor de hoteles seleccionados de una lista de ciudades. Los POI se mostrar\u00e1n en un mapa interactivo de Google. Aqu\u00ed tienes una animaci\u00f3n que muestra los resultados finales.<\/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>Hay algunos giros adicionales para mostrar algunas t\u00e9cnicas m\u00e1s avanzadas.<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>Las ciudades se eligen buscando aeropuertos que tengan hoteles cercanos en la misma ciudad.<\/li>\n<li>Recuperamos los POI mediante una llamada REST, pero los guardamos en nuestra base de datos.<\/li>\n<li>El lado del cliente recibe datos mediante pushes utilizando <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 por el servidor<\/a>.<\/li>\n<\/ul>\n<\/div>\n<div class=\"paragraph\">\n<p>Aunque el c\u00f3digo es corto, muestra varias t\u00e9cnicas con la vinculaci\u00f3n reactiva de datos de Vue y las caracter\u00edsticas de dependencia de propiedades. Combinado con algunas caracter\u00edsticas potentes de Couchbase, tendremos una aplicaci\u00f3n bonita y funcional sin mucho trabajo.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_what_you_need\">Lo que necesita<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>La aplicaci\u00f3n est\u00e1 construida completamente en JavaScript. Para empezar, solo necesitas unas pocas cosas.<\/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\/es\/downloads\/\">Servidor Couchbase<\/a> 5.5.0 o posterior instalado<\/li>\n<\/ol>\n<\/div>\n<div class=\"paragraph\">\n<p>Tambi\u00e9n tendr\u00e1s que conseguir llaves para el <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 de Google Maps<\/a> y el <a title=\"https:\/\/developer.here.com\/develop\/rest-apis\" href=\"https:\/\/developer.here.com\/develop\/rest-apis\">API REST AQU\u00cd<\/a>. Ambos pueden utilizarse gratuitamente (con limitaciones).<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Los datos para la aplicaci\u00f3n vienen como muestra incorporada en la distribuci\u00f3n de Couchbase Server.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_getting_started\">Primeros pasos<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Construiremos la estructura de la aplicaci\u00f3n comenzando con el c\u00f3digo del cliente web. A continuaci\u00f3n viene el lado del servidor Node + c\u00f3digo Express. Finalmente veremos el lado del servidor Couchbase.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Examinaremos con m\u00e1s detalle las consultas N1Ql, incluyendo <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\">ANSI se une<\/a>. Esta aplicaci\u00f3n utiliza el nuevo <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\">Servicio de eventos y funciones<\/a>. Terminaremos viendo el c\u00f3digo JavaScript.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Para empezar, crea un nuevo directorio donde quieras guardar el proyecto. Abra un s\u00edmbolo del sistema y cambie a ese directorio.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_the_web_client_skeleton\">El esqueleto del cliente web<\/h2>\n<div class=\"sectionbody\">\n<div class=\"sect2\">\n<h3 id=\"_generating_the_vue_js_client_scaffolding\">Generaci\u00f3n del andamiaje de cliente Vue.js<\/h3>\n<div class=\"paragraph\">\n<p>El cliente web utiliza <a title=\"https:\/\/vuejs.org\/\" href=\"https:\/\/vuejs.org\">Vue.js<\/a>.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Usaremos el CLI de Vue para crear el proyecto base por nosotros. Voy a mostrar una f\u00e1cil integraci\u00f3n entre el cliente y el lado del servidor con <a title=\"https:\/\/webpack.js.org\/\" href=\"https:\/\/webpack.js.org\/\">webpack<\/a>. Esto significar\u00e1 reordenar un poco los archivos.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Instala Vue CLI usando npm si a\u00fan no lo tienes.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<pre class=\"lang:sh decode:true\">npm install -g @vue\/cli<\/pre>\n<p>Me gusta usar <a title=\"https:\/\/getbootstrap.com\/\" href=\"https:\/\/getbootstrap.com\/\">Bootstrap<\/a>. Hay al menos un par de proyectos por ah\u00ed que integran Boostrap con Vue. Yo eleg\u00ed <a title=\"https:\/\/bootstrap-vue.js.org\/\" href=\"https:\/\/bootstrap-vue.js.org\/\">Bootstrap-Vue<\/a>. Esto no es realmente necesario. No es muy dif\u00edcil eliminar esta dependencia si lo desea.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Crear el boilerplate del proyecto. Aqu\u00ed es donde entra la plantilla webpack simple. El <code>init<\/code> le har\u00e1 algunas preguntas. Utilizar los valores por defecto est\u00e1 bien.<\/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\">Reestructuraci\u00f3n y arreglo<\/h3>\n<div class=\"paragraph\">\n<p>Ahora, cambia al directorio del cliente. Mueve el directorio <code>paquete.json<\/code> y <code>.gitignore<\/code> creados en un nivel superior. As\u00ed se compartir\u00e1n en todo el proyecto.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<pre class=\"lang:sh decode:true\">cd cliente\/\r\nmv paquete.json .gitignore ..<\/pre>\n<p>La configuraci\u00f3n de webpack tambi\u00e9n tiene un peque\u00f1o error. Abrir <code>webpack.config.js<\/code>. En la secci\u00f3n que empieza por<\/p>\n<\/div>\n<div class=\"paragraph\">\n<pre class=\"lang:default decode:true\">        prueba: \/\\.(png|jpg|gif|svg)$\/,<\/pre>\n<p>cambie la l\u00ednea de opciones para que diga<\/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 dependencias y compilar<\/h3>\n<div class=\"paragraph\">\n<p>Inicializar e instalar las dependencias de base.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<pre class=\"lang:sh decode:true\">npm instalar<\/pre>\n<p>Instale nuestras otras dependencias. Muchas de ellas son paquetes est\u00e1ndar (morgan, body-parser). Yo uso <a title=\"https:\/\/github.com\/axios\/axios\" href=\"https:\/\/github.com\/axios\/axios\">axios<\/a> para llamadas de red.\u00a0<a title=\"https:\/\/www.npmjs.com\/package\/sse-channel\" href=\"https:\/\/www.npmjs.com\/package\/sse-channel\">canal sse<\/a> es un buen paquete de eventos enviados por servidor. Es un poco m\u00e1s sofisticado y f\u00e1cil de usar que otros que he probado. Y hay un paquete para facilitar el trabajo con Google Maps en Vue llamado <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 el resto de las dependencias de la siguiente manera. Esto incluye lo que necesitaremos para el 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>Esto le dar\u00e1 un front-end basado en Vue. Para construirlo, ya que hemos movido <code>paquete.json<\/code> subir un nivel, necesitamos retocar la secci\u00f3n de scripts npm. Editar <code>paquete.json<\/code> en la ra\u00edz del proyecto y cambie la l\u00ednea de compilaci\u00f3n a<\/p>\n<\/div>\n<div class=\"paragraph\">\n<pre class=\"lang:default decode:true\">    \"build\": \"cd cliente &amp;&amp; cross-env NODE_ENV=producci\u00f3n webpack --progress --hide-modules &amp;&amp; cp index.html dist\/\"<\/pre>\n<p>Ahora en el cliente haz <code>npm run build<\/code>.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Puede abrir el <code>index.html<\/code> pero no funcionar\u00e1. Saltaremos adelante para crear el servidor, o puedes intentar arreglar el problema aqu\u00ed si s\u00f3lo quieres ver el cliente aut\u00f3nomo.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_the_web_server_skeleton\">El esqueleto del servidor web<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Vuelva a la ra\u00edz del proyecto y prepare el directorio del servidor.<\/p>\n<pre class=\"lang:sh decode:true\">mkdir servidor\r\ncd servidor<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>Vamos a crear el servidor directamente. Inicie la aplicaci\u00f3n base editando un nuevo archivo <code>app.js<\/code>. Pega lo siguiente y gu\u00e1rdalo.<\/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\/\/ captura 404 y reenv\u00eda al gestor de errores\r\napp.use(function(req, res, next) {\r\n  console.dir(req);\r\n  console.dir(res);\r\n  let err = new Error('No encontrado');\r\n  err.status = 404;\r\n  next(err);\r\n});\r\n\r\n\/\/ gestor de errores\r\napp.use(function(err, req, res, next) {\r\n  \/\/ set locals, s\u00f3lo proporciona error en desarrollo\r\n  res.locals.message = err.message;\r\n  res.locals.error = req.app.get('env') === 'development' ? err : {};\r\n\r\n  \/\/ mostrar la p\u00e1gina de error\r\n  res.status(err.status || 500);\r\n  res.render('error');\r\n});\r\n\r\n\/\/ Servidor HTTP\r\nhttp.createServer(app).listen(8080);<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>Esta es una versi\u00f3n simplificada de la final. S\u00f3lo sirve el cliente boilerplate que creamos antes.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>En este punto, deber\u00edas poder ejecutar <code>node app.js<\/code> en el directorio del servidor. Abra una pesta\u00f1a del navegador y vaya a <code><a class=\"bare\" title=\"https:\/\/localhost:8080\/\" href=\"https:\/\/localhost:8080\">https:\/\/localhost:8080<\/a><\/code>. Deber\u00edas ver algo como esto.<\/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\">El cliente y el servidor<\/h2>\n<div class=\"sectionbody\">\n<div class=\"sect2\">\n<h3 id=\"_the_web_client_code\">El c\u00f3digo del cliente web<\/h3>\n<div class=\"paragraph\">\n<p>Ahora volveremos y crearemos el cliente real. En el directorio del cliente, bajo el subdirectorio <code>src<\/code>Abrir el expediente <code>App.vue<\/code>. Actual\u00edzalo como sigue.<\/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>Este es el grueso del c\u00f3digo del lado del cliente.<\/p>\n<p>No voy a entrar en detalles sobre la secci\u00f3n de la plantilla o el css. Se\u00f1alar\u00e9 un buen elemento. La API de Here devuelve, entre otras cosas, enlaces a iconos adecuados para su uso en Mapas. Si sigues el flujo, ver\u00e1s que los marcadores de mapas cargan esos iconos directamente usando las URLs incluidas.<\/p>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_wiring_up_the_vue_databinding\">Conexi\u00f3n de la base de datos Vue<\/h4>\n<div class=\"paragraph\">\n<p>Recorriendo la secci\u00f3n de scripts, ver\u00e1s que hago un uso intensivo de las capacidades reactivas de Vue. Para entender esta parte, te ayudar\u00e1 tener al menos cierta familiaridad con Vue, especialmente con <a title=\"https:\/\/vuejs.org\/v2\/guide\/computed.html\" href=\"https:\/\/vuejs.org\/v2\/guide\/computed.html\">propiedades calculadas y observadores<\/a>, <a title=\"https:\/\/vuejs.org\/v2\/guide\/instance.html\" href=\"https:\/\/vuejs.org\/v2\/guide\/instance.html\">datos, m\u00e9todo y ganchos del ciclo de vida<\/a>.<\/p>\n<p>Hacemos uso de la <code>montado<\/code> para a\u00f1adir un receptor de eventos enviados por el servidor, y para rellenar inicialmente la lista desplegable de ciudades. La parte m\u00e1s pesada de la l\u00f3gica de negocio tiene lugar en la consulta a la base de datos, como veremos.<\/p>\n<p>Veamos c\u00f3mo funciona la selecci\u00f3n de una ciudad. Observa que cada elemento del bot\u00f3n desplegable tiene un receptor de clics que establece <code>seleccionado<\/code> a los datos de la ciudad para esa entrada. Tenemos un m\u00e9todo de vigilancia definido en <code>seleccionado<\/code>. Vue tambi\u00e9n sabe autom\u00e1ticamente que la propiedad calculada <code>mostrar<\/code> depende de <code>seleccionado<\/code>.<\/p>\n<p>Esto significa que cada vez que se selecciona una ciudad a trav\u00e9s del men\u00fa desplegable, se produce una cascada de actividad. Cambiar <code>seleccionado<\/code> causa <code>mostrar<\/code> se vuelva a calcular. Esto, a su vez, establece el texto del bot\u00f3n desplegable, ya que est\u00e1 vinculado a <code>mostrar<\/code>. En <code>seleccionado<\/code>en el m\u00e9todo <code>ver<\/code> actualiza la tabla de hoteles cada vez que se selecciona una nueva ciudad.<\/p>\n<p>La mesa <code>art\u00edculos<\/code> est\u00e1n vinculados a <code>destinationsProvider<\/code> en <code>m\u00e9todos<\/code>. Al actualizar la tabla se ejecuta el c\u00f3digo. Al igual que la lista de ciudades original, obtiene los hoteles mediante una llamada as\u00edncrona a nuestra base de datos a trav\u00e9s de un punto final REST del servidor.<\/p>\n<p>Vue se encarga de mucho aqu\u00ed por nosotros. Por ejemplo, la llamada para refrescar la tabla no recibe los datos inmediatamente. Vue volver\u00e1 a renderizar las partes relevantes del DOM autom\u00e1ticamente cada vez que vuelva la llamada REST. No tenemos que suministrar nada del cableado, aparte de especificar el enlace entre <code>art\u00edculos<\/code> y <code>destinationProvider<\/code>.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_completing_the_web_client\">Completar el Cliente Web<\/h4>\n<div class=\"paragraph\">Para terminar el lado del cliente, tenemos un par de otros pasos cortos que hacer.Tenemos que importar el m\u00f3dulo para ayudar con Google Maps, y el suministro de una clave. Editar <code>main.js<\/code>. A\u00f1ade una l\u00ednea de importaci\u00f3n y dile a Vue que use el nuevo componente. Aqu\u00ed est\u00e1 el 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    clave: 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>Cargamos la clave API de Google Maps desde un archivo <code>config.js<\/code>. Crea ese archivo y por ahora a\u00f1ade este c\u00f3digo de marcador de posici\u00f3n.<\/p>\n<pre class=\"lang:js decode:true\">exportar por defecto {\r\n  googleMapsKey: ''\r\n}<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>Vuelva a crear el proyecto (<code>npm run build<\/code>). Inicie el servidor, recargue el sitio, y deber\u00eda ver el comienzo de nuestro cliente real con este aspecto.<\/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\">El c\u00f3digo del servidor web<\/h3>\n<div class=\"paragraph\">\n<p>A continuaci\u00f3n completaremos la parte del servidor. Nuestro servidor alimenta las p\u00e1ginas web y expone la API REST que necesitamos. La API es en su mayor\u00eda s\u00f3lo la conveniencia de embalaje en torno a la funcionalidad de base de datos.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>En el c\u00f3digo fuente del servidor, sustituya nuestro <code>app.js<\/code> con esto.<\/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('muestra-viaje');\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 registros = require('.\/rutas\/registros');\r\napp.use('\/registros', registros);\r\nconst events = require('.\/routes\/events');\r\napp.use('\/events', events);\r\n\r\n\/\/ captura 404 y reenv\u00eda al gestor de errores\r\napp.use(function(req, res, next) {\r\n  console.dir(req);\r\n  console.dir(res);\r\n  let err = new Error('No encontrado');\r\n  err.status = 404;\r\n  next(err);\r\n});\r\n\r\n\/\/ gestor de errores\r\napp.use(function(err, req, res, next) {\r\n  \/\/ set locals, s\u00f3lo proporciona error en desarrollo\r\n  res.locals.message = err.message;\r\n  res.locals.error = req.app.get('env') === 'development' ? err : {};\r\n\r\n  \/\/ mostrar la p\u00e1gina de error\r\n  res.status(err.status || 500);\r\n  res.render('error');\r\n});\r\n\r\n\/\/ Servidor HTTP\r\n\r\nconst http_port = process.env.HTTP_PORT;\r\nconst servidor_http = http.createServer(app);\r\n\r\nhttp_server.listen(puerto_http);\r\n\r\nhttp_server.on('error', onError);\r\nhttp_server.on('escuchando', onListening);\r\n\r\n\/\/ Servidor HTTPS\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 = proceso.env.HTTPS_PORT;\r\nconst https_server = https.createServer(options, app);\r\n\r\nhttps_server.listen(puerto_https);\r\n\r\nhttps_server.on('error', onError);\r\nhttps_server.on('escuchando', onListening);\r\n\r\n\/**\r\n * Escuchador de eventos para el evento \"error\" del servidor HTTP\/S.\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 puerto === 'cadena'\r\n    ? 'Tuber\u00eda ' + puerto\r\n    : 'Puerto ' + puerto;\r\n\r\n  \/\/ manejar errores de escucha espec\u00edficos con mensajes amigables\r\n  switch (error.code) {\r\n    case 'EACCES':\r\n      console.error(bind + ' requiere privilegios elevados');\r\n      process.exit(1);\r\n      break;\r\n    case 'EADDRINUSE':\r\n      console.error(bind + ' ya est\u00e1 en uso');\r\n      process.exit(1);\r\n      break;\r\n    por defecto:\r\n      throw error;\r\n  }\r\n}\r\n\r\n\/**\r\n * Listener de eventos para el evento \"listening\" del servidor HTTP\/S.\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    : 'puerto ' + addr.port;\r\n  debug('Escuchando en ' + bind);\r\n}<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>Las diferencias clave son la configuraci\u00f3n del cliente Couchbase Server Node, y el cableado de las rutas para los puntos finales REST. Hay otro c\u00f3digo adicional para cosas como servir sobre http y https, tambi\u00e9n. No veremos esas partes.<\/p>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_connecting_to_couchbase_server\">Conexi\u00f3n a Couchbase Server<\/h4>\n<div class=\"paragraph\">\n<p>Los dos bloques de c\u00f3digo para conectar con nuestra base de datos son muy sencillos.<\/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>Las tres primeras l\u00edneas importan el cliente Couchbase Node, crean un nuevo objeto cluster que representa un cluster de nodos de base de datos, y autentifican a ese cluster. Esto inicia la conexi\u00f3n a la base de datos.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Para mayor comodidad, a\u00f1adimos referencias a los objetos cliente y cluster a <code>app.locales<\/code>. Esto hace que est\u00e9n disponibles en todo el mundo.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Por \u00faltimo, el c\u00f3digo establece y guarda conexiones con dos cubos. <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\">Cubos<\/a> son una estructura organizativa de alto nivel en Couchbase.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>El primer bucket lo rellenaremos con datos de ejemplo que vienen con las instalaciones de Couchbase Server. Para el segundo cubo, estoy ama\u00f1ando un poco las cosas aqu\u00ed. Necesitamos un bucket de metadatos para el archivo <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\">Servicio de concursos<\/a>. Como veremos, s\u00f3lo necesitamos almacenar un par de documentos adicionales que deben ir a alg\u00fan sitio aparte del cubo principal. En lugar de crear un tercer bucket, simplemente los pongo con los datos de eventos. Normalmente no usar\u00edas este atajo en producci\u00f3n.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_static_files_and_api_routes\">Archivos est\u00e1ticos y rutas API<\/h4>\n<div class=\"paragraph\">\n<p>Tenemos s\u00f3lo unas pocas l\u00edneas de c\u00f3digo que necesitamos para dirigir Express para servir nuestras p\u00e1ginas est\u00e1ticas construidas desde el c\u00f3digo del cliente y para organizar nuestra API de datos del servidor.<\/p>\n<pre class=\"lang:js decode:true\">app.use(express.static(path.join(__dirname, '..\/client')));\r\n\r\nconst registros = require('.\/rutas\/registros');\r\napp.use('\/registros', registros);\r\nconst events = require('.\/routes\/events');\r\napp.use('\/events', events);<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>La plantilla <code>index.html<\/code> La p\u00e1gina de inicio de la aplicaci\u00f3n a\u00f1ade <code>dist<\/code> a todas las rutas de los archivos. Esto significa que nuestros archivos est\u00e1ticos en realidad se sirven desde un directorio ra\u00edz de <code>\/cliente\/dist<\/code>.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>He separado la API de datos en dos grupos, organizados bajo un ep\u00edgrafe general <code>rutas<\/code> subdirectorio. Los puntos finales empiezan por <code>registros<\/code>. Estos recuperar\u00e1n datos de la base de datos.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>En <code>eventos<\/code> es \u00fanica. Los endpoints son utilizados tanto por el cliente web como por el servicio de eventos de Couchbase.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Veamos la <code>registros<\/code> c\u00f3digo primero.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4>API de acceso a bases de datos<\/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>Aqu\u00ed tenemos definidas tres rutas, <code>\/destinos<\/code>, <code>\/hoteles\/porCiudad\/:id<\/code>y <code>\/selecci\u00f3n\/geo<\/code>. Todos tienen la misma estructura b\u00e1sica. Obtenemos nuestras referencias a la base de datos, utilizamos bluebird para crear una promesa versiones del m\u00e9todo de consulta, construir un <a title=\"https:\/\/www.couchbase.com\/n1ql\" href=\"https:\/\/www.couchbase.com\/blog\/es\/n1ql\/\">N1QL<\/a> dispararla y devolver los resultados.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Repasemos las consultas, empezando por la m\u00e1s sencilla.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_n1ql_queries\">Consultas N1QL<\/h4>\n<div class=\"paragraph\">\n<p>Utilizamos La <code>\/selecci\u00f3n\/geo<\/code> para almacenar la elecci\u00f3n de hotel realizada por el usuario. Aqu\u00ed est\u00e1 la consulta desglosada.<\/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 un documento, o lo crear\u00e1 si a\u00fan no existe. Almacenamos la geolocalizaci\u00f3n del hotel elegido en un documento con un id de <code>desencadenar<\/code>. Probablemente suene extra\u00f1o. Tendr\u00e1 m\u00e1s sentido m\u00e1s adelante, cuando lleguemos al c\u00f3digo Eventing. Lo que realmente nos interesa no es s\u00f3lo la ubicaci\u00f3n del hotel, sino los puntos de inter\u00e9s cercanos. Este documento pondr\u00e1 en marcha la secuencia que recupera esos POI. De ah\u00ed la raz\u00f3n de llamar al documento <code>desencadenar<\/code>.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>He aqu\u00ed un ejemplo del documento creado.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"title\">\n<p><strong>desencadenar<\/strong><\/p>\n<pre class=\"lang:js decode:true\">{\r\n  \"precisi\u00f3n\": \"APROXIMADA\",\r\n  \"lat\": 43.9397954,\r\n  \"lon\": 4.805895400000054\r\n}<\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>Para comprender la <code>\/hoteles\/porCiudad\/:id<\/code> consulta, primero eche un vistazo a un par de documentos de ejemplo.<\/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  \"direcci\u00f3n\": \"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\": \"Hotel de gesti\u00f3n familiar con vistas a un jard\u00edn florido, dentro de un aparcamiento privado. Internet wi-fi disponible en todo el edificio. Habitaciones recientemente renovadas con el t\u00edpico estilo provenzal. Se hablan 7 idiomas. Servicio de taxi privado\",\r\n  \"C\u00f3mo llegar\": \"justo detr\u00e1s de la estaci\u00f3n central, que da a la avenida principal del centro y la estaci\u00f3n de autobuses\",\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    \"precisi\u00f3n\": \"APROXIMADA\",\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\": \"Habitaci\u00f3n doble con ba\u00f1o y ducha 30-60\u20ac, estudios y apartamentos desde 75\u20ac, desayuno 7\u20ac se puede tomar en el jard\u00edn en temporada 7:30AM 11AM\",\r\n  \"public_likes\": [\"Vicente Williamson\"],\r\n  \"reviews\": [...],\r\n  \"state\": \"Provence-Alpes-C\u00f4te d'Azur\",\r\n  \"t\u00edtulo\": \"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>aeropuerto_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\": \"aeropuerto\",\r\n  \"tz\": \"Europe\/Paris\"\r\n}<\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>Para nuestra tabla de hoteles, necesitamos el nombre del hotel, la direcci\u00f3n, la geolocalizaci\u00f3n, el nombre del aeropuerto y el c\u00f3digo del aeropuerto. Obviamente, eso es combinar datos de ambos documentos. Para ello utilizamos un <code>INNER JOIN<\/code>. Esta es la consulta.<\/p>\n<pre class=\"lang:default decode:true\">SELECT hotel.name, hotel.address, airport.airportname, airport.icao, hotel.geo\r\nFROM `viajes-muestra` aeropuerto\r\nINNER JOIN `muestra-de-viaje` hotel\r\n  ON hotel.type = 'hotel' AND hotel.city = airport.city\r\nWHERE aeropuerto.tipo = 'aeropuerto\r\n  AND airport.city = '${req.params.id}''\r\nLIMIT 5;<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>Caminando a trav\u00e9s de \u00e9l, se puede ver que somos capaces de realizar la uni\u00f3n utilizando los documentos de la misma cubeta. Utilizo alias para hacer las cosas m\u00e1s claras. Utilizamos la ciudad de cada documento para formar la condici\u00f3n de uni\u00f3n. Observa que tambi\u00e9n utilizo el documento <code>tipo<\/code>tanto en la condici\u00f3n de uni\u00f3n como en la de <code>DONDE<\/code> cl\u00e1usula. Las condiciones de uni\u00f3n pueden ser bastante sofisticadas. Lea esto <a title=\"https:\/\/www.couchbase.com\/blog\/ansi-join-support-n1ql\/\" href=\"https:\/\/www.couchbase.com\/blog\/es\/ansi-join-support-n1ql\/\">blog<\/a> para m\u00e1s detalles y ejemplos.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Por \u00faltimo, examinemos c\u00f3mo hemos obtenido nuestra lista de ciudades. Esta es la consulta para el <code>\/destinos<\/code>punto final.<\/p>\n<pre class=\"lang:default decode:true\">SELECT DISTINCT aeropuerto.ciudad como nombre\r\nFROM `viajes-muestra` aeropuerto\r\nINNER JOIN `viajes-muestra` hotel\r\nUSE HASH(sonda)\r\n  ON hotel.city = airport.city\r\nWHERE aeropuerto.tipo = 'aeropuerto\r\n  AND hotel.type = 'hotel';<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>El \u00fanico resultado devuelto es una lista de nombres de ciudades. En este caso, estamos utilizando una uni\u00f3n interna como filtro. Al emparejar las ciudades con aeropuertos con las ciudades con hoteles, obtenemos una lista de las ciudades que tienen ambos.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Las uniones internas pueden utilizar dos enfoques algor\u00edtmicos diferentes. La primera uni\u00f3n que vimos utiliza la uni\u00f3n de bucle anidado por defecto.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Este \u00faltimo ejemplo utiliza una tabla hash en memoria. Esto puede acelerar considerablemente una uni\u00f3n, sobre todo cuando uno de los dos conjuntos de datos es peque\u00f1o. Hemos utilizado la tabla <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 a N1QL de c\u00f3mo queremos optimizar la consulta. Hay un lado \"probe\" y un lado \"build\". La tabla hash se construye a partir de los datos del lado de construcci\u00f3n. La uni\u00f3n se realiza haciendo b\u00fasquedas en los datos del lado de la sonda.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>La pista que dimos arriba le dice a N1QL que use los datos del hotel para el lado de la sonda en este caso. Es decir, se construir\u00e1 la tabla a partir de los datos del aeropuerto, a continuaci\u00f3n, hacer las b\u00fasquedas hash utilizando los datos del hotel.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Si no lo has hecho antes, te animo a que pruebes estas consultas directamente en el Couchbase Server Query Workbench, parte de la consola de administraci\u00f3n web.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_server_sent_events\">Eventos enviados por el servidor<\/h4>\n<div class=\"paragraph\">\n<p>Ya hemos mencionado la creaci\u00f3n de un receptor 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 por el servidor<\/a> en el lado del cliente. Estos dos puntos finales muestran lo que se necesita en el servidor.<\/p>\n<pre class=\"lang:js decode:true\">const express = require('express');\r\nconst router = express.Router();\r\nconst sse = require('sse-canal');\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 = { evento: '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 = enrutador;<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>La versi\u00f3n \"get\" del <code>poi<\/code> es llamado por el navegador cuando el <code>EventSource<\/code> se construye. Puedes ver que simplemente a\u00f1adimos la persona que llama como cliente.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Utilizamos la versi\u00f3n \"post\" como intermediaria para enviar los datos al cliente. El sitio <code>res.send('');<\/code> nos da una pista de c\u00f3mo funciona. En el c\u00f3digo Eventing, vamos a utilizar las capacidades N1QL cURL para empujar los datos a este punto final. La respuesta vac\u00eda est\u00e1 ah\u00ed para cerrar esa transacci\u00f3n.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>A continuaci\u00f3n, el servidor reenv\u00eda los datos a los clientes que est\u00e9n a la escucha. Hay muchos m\u00e1s detalles. Si quieres saber m\u00e1s, <a title=\"https:\/\/www.html5rocks.com\/en\/tutorials\/eventsource\/basics\/\" href=\"https:\/\/www.html5rocks.com\/en\/tutorials\/eventsource\/basics\/\">este art\u00edculo<\/a> tiene buena informaci\u00f3n.<\/p>\n<\/div>\n<\/div>\n<div class=\"sect3\">\n<h4 id=\"_finishing_the_server\">Acabado del servidor<\/h4>\n<div class=\"paragraph\">\n<p>Para terminar el lado servidor de nuestro proyecto, crea un subdirectorio <code>rutas<\/code> en el directorio del servidor.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Copie el <code>registros<\/code> en un archivo llamado <code>registros.js<\/code>. Copia el <code>eventos<\/code> en un archivo llamado <code>eventos.js<\/code>. Y, por \u00faltimo, en el propio directorio del servidor, cree un nuevo archivo llamado <code>.env<\/code>. Pegue all\u00ed los siguientes par\u00e1metros de configuraci\u00f3n y gu\u00e1rdelos. (Por supuesto, cambie los par\u00e1metros que necesite).<\/p>\n<pre class=\"lang:default decode:true\">HTTP_PORT=8080\r\nPUERTO_HTTPS=8081\r\nDEBUG=nodo,http,poi:*\r\nCLUSTER='couchbase:\/\/localhost:8091'\r\nCLUSTER_USER=Administrador\r\nCLUSTER_PASSWORD=contrase\u00f1a<\/pre>\n<\/div>\n<div class=\"paragraph\">\n<p>El servidor ya deber\u00eda estar listo. En el directorio ra\u00edz del servidor, ejecute <code>node app.js<\/code>. No olvides construir primero el c\u00f3digo cliente.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_the_couchbase_server_eventing_service_code\">El c\u00f3digo del servicio de eventos del servidor Couchbase<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Aqu\u00ed est\u00e1 la \u00faltima parte de la salsa especial que hace que esta aplicaci\u00f3n funcione. En la versi\u00f3n 5.5.0, Couchbase introdujo la funci\u00f3n <a title=\"https:\/\/www.couchbase.com\/blog\/eventing\/\" href=\"https:\/\/www.couchbase.com\/blog\/es\/eventing\/\">Servicio de concursos<\/a>. Esta es probablemente mi nueva caracter\u00edstica favorita en la serie de lanzamientos 5.5. Couchbase Functions es el primer componente ofrecido como parte de este servicio. En resumen, Functions te permite ejecutar c\u00f3digo <strong>en el servidor de base de datos<\/strong> en respuesta a los cambios en la base de datos.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Las funciones est\u00e1n escritas en JavaScript est\u00e1ndar, con algunos <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\">adiciones y restricciones<\/a>. Para crear la funci\u00f3n que necesitamos, sigue estos pasos.<\/p>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_eventing_meta_data_bucket\">Cubo de metadatos de eventos<\/h3>\n<div class=\"paragraph\">\n<p>En primer lugar, cree un bucket para los metadatos de eventing.<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>Abra la consola de Couchbase Server e inicie sesi\u00f3n si es necesario<\/li>\n<li>Haga clic en \"Cubos\" en el men\u00fa de la izquierda<\/li>\n<li>Haga clic en \"A\u00f1adir cubo\" en la esquina superior derecha<\/li>\n<li>Entre en <code>concurso<\/code> para el nombre del cubo en el cuadro de di\u00e1logo que aparece<\/li>\n<li>Haga clic en \"A\u00f1adir cubo\" para terminar<\/li>\n<\/ul>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_adding_a_function\">A\u00f1adir una funci\u00f3n<\/h3>\n<div class=\"paragraph\">\n<p>Ahora, configura la funci\u00f3n y a\u00f1ade el c\u00f3digo.<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>Haga clic en \"Eventos\" en el men\u00fa de la izquierda<\/li>\n<li>Haga clic en \"A\u00f1adir funci\u00f3n\" en la esquina superior derecha<\/li>\n<\/ul>\n<\/div>\n<div class=\"paragraph\">\n<p>Aparecer\u00e1 un cuadro de di\u00e1logo.<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>Seleccione <code>viaje-muestra<\/code> como cubo de origen<\/li>\n<li>Seleccione <code>concurso<\/code> como cubo de metadatos<\/li>\n<li>Entre en <code>monitor<\/code> (o lo que desee) para el Nombre de la funci\u00f3n<\/li>\n<li>En \"Fijaciones\", seleccione <code>tipo<\/code> a \"Alias\", <code>nombre<\/code> a \"muestra-viaje\", y <code>valor<\/code> a \"db\"<\/li>\n<li>Haga clic en \"Siguiente: A\u00f1adir 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>Esto le llevar\u00e1 al editor de c\u00f3digo. Est\u00e1 pre-llenado con las firmas de funci\u00f3n. Copie este c\u00f3digo en su lugar.<\/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\">Despliegue de una funci\u00f3n<\/h3>\n<div class=\"paragraph\">\n<p>Para desplegar este c\u00f3digo, primero haga clic en \"Guardar\" y, a continuaci\u00f3n, vuelva a hacer clic en \"Eventos\" en el men\u00fa de la izquierda. Deber\u00edas ver una entrada para la funci\u00f3n. Haz clic en cualquier lugar de esa barra. Deber\u00edas verla expandida.<\/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>Haga clic en \"Desplegar\" y, a continuaci\u00f3n, en \"Desplegar funci\u00f3n\".<\/p>\n<\/div>\n<\/div>\n<div class=\"sect2\">\n<h3 id=\"_understanding_the_function_code\">Comprender el c\u00f3digo de funci\u00f3n<\/h3>\n<div class=\"paragraph\">\n<p><code>OnUpdate<\/code> se ejecuta cada vez que cambia un documento. Recibe el documento y los metadatos del documento como par\u00e1metros.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Buscamos el <code>desencadenar<\/code> documento a cambiar, indicando la selecci\u00f3n de un nuevo hotel. La primera l\u00ednea filtra todos los dem\u00e1s documentos bas\u00e1ndose en el identificador del documento (a veces denominado clave del documento).<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>La siguiente l\u00ednea muestra algunas cosas interesantes. Recupere <code>db<\/code> es un alias para el cubo de muestras de viajes. <code>db['aqu\u00ed']<\/code> recupera directamente un documento con id <code>aqu\u00ed<\/code>. Aqu\u00ed es donde almacenaremos las credenciales necesarias para el <a title=\"https:\/\/www.here.com\/\" href=\"https:\/\/www.here.com\/\">AQU\u00cd<\/a> servicios de cartograf\u00eda.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Preparamos la URL y los datos para nuestra solicitud de puntos de inter\u00e9s. Here tiene un mont\u00f3n de funcionalidades interesantes en su API. S\u00f3lo vamos a hacer una petici\u00f3n b\u00e1sica.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Con esa informaci\u00f3n en la mano, estamos listos para nuestra llamada cURL. Construyendo la consulta N1QL, vemos una de las modificaciones al JavaScript est\u00e1ndar: Puedes escribir tus consultas en l\u00ednea de la misma manera que las construir\u00edas en el Query Workbench.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Vemos otro bonito detalle en la consulta cURL. N1QL proporciona una sintaxis conveniente para filtrar los resultados. A\u00f1adiendo la ruta <code>.resultados.item<\/code> hasta el final, cogemos s\u00f3lo los datos que queremos.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>A continuaci\u00f3n ejecutamos la consulta y, utilizando esa misma <code>db[]<\/code> taquigraf\u00eda, actualice nuestro <code>poi<\/code> documento. \u00c9ste es un ejemplo de utilizaci\u00f3n de una funci\u00f3n para aumentar los datos. En otro escenario, podr\u00edamos derivar nuestra actualizaci\u00f3n enteramente de los registros de la base de datos. Por ejemplo, podr\u00eda rellenar todos los detalles de un carro de la compra a medida que un cliente hace selecciones.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Por \u00faltimo, con nuestros puntos de inter\u00e9s en la mano, volvemos a utilizar cURL para empujar los datos a nuestro punto final del servidor web. Recordemos que la versi\u00f3n \"post\" de <code>poi<\/code> API ingiere los datos entrantes y los devuelve a los clientes registrados. As\u00ed podemos hacer que la interfaz de usuario del cliente reaccione a los cambios de la base de datos sin tener que sondear.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_final_steps\">Pasos finales<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Ya estamos listos para montarlo todo. Puedes probar la aplicaci\u00f3n tal cual, pero la parte de los mapas no funcionar\u00e1 todav\u00eda. Para ello, se necesita una clave de API de Google Maps, y un conjunto de credenciales de HERE.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>La tecla Mapas va en el <code>config.js<\/code> en el c\u00f3digo del cliente. Guarde las claves AQU\u00cd en un documento en el <code>concurso<\/code> cubo. Puede hacerlo directamente en la consola de administraci\u00f3n haciendo clic en \"Documentos\" en el men\u00fa de la izquierda y luego en \"A\u00f1adir documento\" en la parte superior derecha. Utilice esto como plantilla.<\/p>\n<\/div>\n<div class=\"listingblock\">\n<div class=\"title\">\n<p><strong><strong>aqu\u00ed<\/strong><\/strong><\/p>\n<pre class=\"lang:js decode:true\">{\r\n  \"id\": \"TPxxxxxxxxxxxxxx\",\r\n  \"code\": \"whsxxxxxxxxxxxxxxx\"\r\n}<\/pre>\n<\/div>\n<\/div>\n<div class=\"paragraph\">\n<p>Y, por \u00faltimo, como medida de seguridad, cURL est\u00e1 desactivado por defecto. En la consola de administraci\u00f3n, haga lo siguiente.<\/p>\n<\/div>\n<div class=\"ulist\">\n<ul>\n<li>Haga clic en \"Configuraci\u00f3n\" en el men\u00fa de la izquierda<\/li>\n<li>Haga clic para ampliar \"Configuraci\u00f3n avanzada de consultas\"<\/li>\n<li>Seleccione \"Sin restricciones\" en \"Acceso a la funci\u00f3n CURL()\".<\/li>\n<\/ul>\n<\/div>\n<div class=\"paragraph\">\n<p>Esto es <strong>no<\/strong> lo que usted quiere para la producci\u00f3n. En su lugar, usted querr\u00eda una lista blanca de un conjunto selecto de URLs. Sin embargo, esto servir\u00e1 para nuestro proyecto.<\/p>\n<\/div>\n<div class=\"paragraph\">\n<p>Con eso, en el directorio del servidor web, ejecute <code>node app.js<\/code>. Abrir <code>localhost:8080<\/code> en su navegador (o lo que haya elegido en <code>.env<\/code>) y pru\u00e9balo.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_source\">Fuente<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Puedes encontrar el c\u00f3digo fuente de toda la aplicaci\u00f3n en GitHub <a title=\"https:\/\/github.com\/couchbaselabs\/points-of-interest\" href=\"https:\/\/github.com\/couchbaselabs\/points-of-interest\">aqu\u00ed<\/a>. He incluido un script <code>configuraci\u00f3n<\/code> para simplificar la preparaci\u00f3n de todo. S\u00f3lo tienes que ejecutar <code>.\/configuraci\u00f3n<\/code> e introduzca sus claves. (Puede que tengas que hacerlo ejecutable primero.) Todav\u00eda necesitas ejecutar <code>npm instalar<\/code>y construir el c\u00f3digo del 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>Esta aplicaci\u00f3n se utiliz\u00f3 como parte de una demostraci\u00f3n en un webinar de Couchbase. Puedes ver una grabaci\u00f3n del mismo <a title=\"https:\/\/event.on24.com\/wcc\/r\/1774871\/5A84BC9DF67C6113F8DD95AD7DF71BBA\" href=\"https:\/\/event.on24.com\/wcc\/r\/1774871\/5A84BC9DF67C6113F8DD95AD7DF71BBA\">aqu\u00ed<\/a>. No deje de consultar otros <a title=\"https:\/\/www.couchbase.com\/resources\/webinars\" href=\"https:\/\/www.couchbase.com\/blog\/es\/resources\/webinars\/\">seminarios web<\/a> en el \u00e1rea de recursos de Couchbase.<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"sect1\">\n<h2 id=\"_postscript\">Posdata<\/h2>\n<div class=\"sectionbody\">\n<div class=\"paragraph\">\n<p>Couchbase es de c\u00f3digo abierto y <a title=\"https:\/\/www.couchbase.com\/downloads\" href=\"https:\/\/www.couchbase.com\/blog\/es\/downloads\/\">probar gratis<\/a>.<br \/>\n<strong>Empezar<\/strong> con <a title=\"https:\/\/www.couchbase.com\/get-started\" href=\"https:\/\/developer.couchbase.com\/tutorials\">c\u00f3digo de ejemplo, consultas de ejemplo, tutoriales y mucho m\u00e1s<\/a>.<br \/>\nM\u00e1s recursos en nuestra <a title=\"https:\/\/developer.couchbase.com\/community\" href=\"https:\/\/developer.couchbase.com\/community\">portal para desarrolladores<\/a>.<br \/>\nS\u00edguenos en Twitter <a title=\"https:\/\/twitter.com\/CouchbaseDev\" href=\"https:\/\/twitter.com\/CouchbaseDev\">@CouchbaseDev<\/a>.<br \/>\nPuede enviar preguntas a nuestro <a title=\"https:\/\/www.couchbase.com\/forums\/\" href=\"https:\/\/www.couchbase.com\/blog\/es\/forums\/\">foros<\/a>.<br \/>\nParticipamos activamente en <a title=\"https:\/\/stackoverflow.com\/questions\/tagged\/couchbase\" href=\"https:\/\/stackoverflow.com\/questions\/tagged\/couchbase\">Stack Overflow<\/a>.<br \/>\nEnv\u00edame tus preguntas, comentarios, temas que te gustar\u00eda ver, etc. a Twitter. <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\/es\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/\" \/>\n<meta property=\"og:locale\" content=\"es_MX\" \/>\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\/es\/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\":\"es\",\"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\":\"es\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@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\":\"es\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#organization\",\"name\":\"The Couchbase Blog\",\"url\":\"https:\/\/www.couchbase.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"es\",\"@id\":\"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/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\":\"es\",\"@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\/es\/author\/hod-greeley\/\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Build an App with Vue.js, Node.js, Express & Couchbase","description":"Mapea puntos de inter\u00e9s con este tutorial completo utilizando Vue.js, Node.js, Express y 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\/es\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/","og_locale":"es_MX","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\/es\/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":"es","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":"Mapea puntos de inter\u00e9s con este tutorial completo utilizando Vue.js, Node.js, Express y Couchbase.","breadcrumb":{"@id":"https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/#breadcrumb"},"inLanguage":"es","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.couchbase.com\/blog\/tutorial-points-interest-app-vuejs-nodejs-express-couchbase-server\/"]}]},{"@type":"ImageObject","inLanguage":"es","@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":"El blog de Couchbase","description":"Couchbase, la base de datos 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":"es"},{"@type":"Organization","@id":"https:\/\/www.couchbase.com\/blog\/#organization","name":"El blog de Couchbase","url":"https:\/\/www.couchbase.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"es","@id":"https:\/\/www.couchbase.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/www.couchbase.com\/blog\/wp-content\/uploads\/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, Defensor del Desarrollador, Couchbase","image":{"@type":"ImageObject","inLanguage":"es","@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 es desarrollador de Couchbase y vive en Silicon Valley. Tiene m\u00e1s de dos d\u00e9cadas de experiencia como ingeniero de software y director de ingenier\u00eda. Ha trabajado en una variedad de campos de software, incluyendo f\u00edsica computacional y qu\u00edmica, seguridad inform\u00e1tica y de redes, finanzas y m\u00f3viles. Antes de unirse a Couchbase en 2016, Hod dirigi\u00f3 las relaciones con desarrolladores para m\u00f3viles en Samsung. Hod es doctor en f\u00edsica qu\u00edmica por la Universidad de Columbia.","sameAs":["https:\/\/hod.greeley.org\/blog","https:\/\/x.com\/HodGreeley"],"url":"https:\/\/www.couchbase.com\/blog\/es\/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 es desarrollador de Couchbase y vive en Silicon Valley. Tiene m\u00e1s de dos d\u00e9cadas de experiencia como ingeniero de software y director de ingenier\u00eda. Ha trabajado en una variedad de campos de software, incluyendo f\u00edsica computacional y qu\u00edmica, seguridad inform\u00e1tica y de redes, finanzas y m\u00f3viles. Antes de unirse a Couchbase en 2016, Hod dirigi\u00f3 las relaciones con desarrolladores para m\u00f3viles en Samsung. Hod es doctor en f\u00edsica qu\u00edmica por la Universidad de Columbia."}],"_links":{"self":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/5538","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/users\/73"}],"replies":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/comments?post=5538"}],"version-history":[{"count":0,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/posts\/5538\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media\/5541"}],"wp:attachment":[{"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/media?parent=5538"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/categories?post=5538"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/tags?post=5538"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/www.couchbase.com\/blog\/es\/wp-json\/wp\/v2\/ppma_author?post=5538"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}