{"id":13596,"date":"2018-05-09T17:10:03","date_gmt":"2018-05-09T20:10:03","guid":{"rendered":"https:\/\/king.host\/blog\/?p=13596"},"modified":"2024-06-03T16:52:59","modified_gmt":"2024-06-03T19:52:59","slug":"dashboard-node-js-socketio-vuejs","status":"publish","type":"post","link":"https:\/\/king.host\/blog\/tecnologia\/dashboard-node-js-socketio-vuejs\/","title":{"rendered":"Dashboard em real time com NODE JS, SOCKET.IO e VUEJS"},"content":{"rendered":"<p><strong>Node JS se tornou popular devido a sua facilidade em trabalhar com real-time<\/strong> e baixa performance do sistema, simplesmente por que o protocolo WebSockets utiliza o Javascript.<\/p>\n<p><strong>Com WebSockets, um cliente e um servidor podem \u201cconversar\u201c entre si em tempo real<\/strong>, como se estivessem realizando uma chamada telef\u00f4nica. Uma vez conectado, um cliente poder\u00e1 receber dados do servidor, sem precisar fazer nada. Diferente dos protocolos WEB onde precisam ser atualizados para receber uma nova informa\u00e7\u00e3o do servidor. Por outro lado, <strong>o servidor tamb\u00e9m poder\u00e1 receber dados em tempo real do cliente dentro da mesma conex\u00e3o<\/strong>.<\/p>\n<p><strong>Neste artigo vou mostrar<\/strong> algo muito \u00fatil e com muito poder para os dias de hoje que \u00e9 <strong>um\u00a0painel anal\u00edtico em tempo real com Node JS, Socket.io e VueJs<\/strong> para monitorar o acesso \u00e0s p\u00e1ginas do site, semelhante ao Google Analytics.<\/p>\n<p><img fetchpriority=\"high\" decoding=\"async\" class=\"aligncenter wp-image-13598 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img1.png\" alt=\"\" width=\"951\" height=\"283\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img1.png 951w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img1-300x89.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img1-768x229.png 768w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img1-780x232.png 780w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img1-400x119.png 400w\" sizes=\"(max-width: 951px) 100vw, 951px\" \/><\/p>\n<p>Descompacte o arquivo real time na sua pasta do node. No meu caso, estou usando ela em <strong>\/opt\/lampp\/htdocs\/node<\/strong>.<\/p>\n<p>Ap\u00f3s descompactar inicialize um novo arquivo <strong>package.json<\/strong> via terminal com:<\/p>\n<p><strong>npm init<\/strong><\/p>\n<p>Ap\u00f3s algumas perguntas, ele inicia criar\u00e1 o package.json na raiz da aplica\u00e7\u00e3o.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter wp-image-13599 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img2.png\" alt=\"\" width=\"781\" height=\"738\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img2.png 781w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img2-300x283.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img2-768x726.png 768w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img2-780x737.png 780w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img2-400x378.png 400w\" sizes=\"(max-width: 781px) 100vw, 781px\" \/><\/p>\n<p>Depois de inicializar o package.json, instale ExpressJs e Socket.io da seguinte forma:<\/p>\n<p><strong>npm install &#8212; save express socket.io<\/strong><\/p>\n<p>Aqui est\u00e1 como a estrutura final do projeto. Vamos construindo essa estrutura conforme avan\u00e7amos.<\/p>\n<p><img decoding=\"async\" class=\"aligncenter wp-image-13600 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img3.png\" alt=\"\" width=\"352\" height=\"380\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img3.png 352w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img3-278x300.png 278w\" sizes=\"(max-width: 352px) 100vw, 352px\" \/><\/p>\n<p>Os arquivos e diret\u00f3rios que valem a pena serem notados s\u00e3o:<\/p>\n<p><strong>\/public\/directory<\/strong> &#8211; Cont\u00e9m nossos ativos est\u00e1ticos<br \/>\n<strong>\/js\/dashboard.js<\/strong> &#8211; Cont\u00e9m o c\u00f3digo VueJs e Socket.io para receber e renderizar as estat\u00edsticas no painel<br \/>\n<strong>\/css\/dashboard.css<\/strong> &#8211; Tem algum estilo b\u00e1sico para o painel<br \/>\n<strong>\/views\/dashboard.html<\/strong> &#8211; Cont\u00e9m diretivas HTML e Vue para vincula\u00e7\u00e3o e renderiza\u00e7\u00e3o de dados<br \/>\n<strong>\/views\/index.html<\/strong> &#8211; \u00c9 a p\u00e1gina que um visitante recebe e cont\u00e9m o c\u00f3digo de acompanhamento<br \/>\n<strong>app.js<\/strong> &#8211; Cont\u00e9m nossa l\u00f3gica do lado do servidor<br \/>\n<strong>config.js<\/strong> &#8211; \u00c9 onde vamos armazenar qualquer assunto de configura\u00e7\u00e3o para alterar<\/p>\n<p>Ao longo do tutorial, vou falar da maior parte deles.<\/p>\n<h2>Configurando o Node JS e Socket.IO<\/h2>\n<p>Nesta se\u00e7\u00e3o, <strong>vamos configurar a estrutura b\u00e1sica do nosso servidor Node + Socket.io<\/strong> e configur\u00e1-la para ouvir as conex\u00f5es de entrada do soquete.<\/p>\n<p>No arquivo da raiz app.js cont\u00e9m as configura\u00e7\u00f5es do lado servidor:<\/p>\n<p>Altere a porta 3000 para a porta que voc\u00ea definiu para o Node Js.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13601 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img4.png\" alt=\"\" width=\"653\" height=\"40\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img4.png 653w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img4-300x18.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img4-400x25.png 400w\" sizes=\"(max-width: 653px) 100vw, 653px\" \/><\/p>\n<p><em>app.set(&#8216;port&#8217;, (process.env.PORT || 3000));<\/em><\/p>\n<p>O objeto visitorsData armazena as informa\u00e7\u00f5es coletadas sobre cada usu\u00e1rio e as associar\u00e1 a um ID de soquete exclusivo. Isso servir\u00e1 como uma maneira exclusiva de identificar a conex\u00e3o de soquete de um usu\u00e1rio com os dados coletados sobre eles.<\/p>\n<p>\u00c9 importante ressaltar que voc\u00ea n\u00e3o ir\u00e1 servir todas as p\u00e1ginas acessadas e sim as que est\u00e3o definidas no app.js, que s\u00e3o:<br \/>\n<em>\/<\/em><br \/>\n<em>\/about<\/em><br \/>\n<em>\/contact<\/em><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13602 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img5.png\" alt=\"\" width=\"833\" height=\"99\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img5.png 833w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img5-300x36.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img5-768x91.png 768w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img5-780x93.png 780w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img5-400x48.png 400w\" sizes=\"(max-width: 833px) 100vw, 833px\" \/><\/p>\n<p><em>app.get(&#8216;\/dashboard&#8217;, function(req, res) {<\/em><br \/>\n<em>res.sendFile(path.join(__dirname, &#8216;views\/dashboard.html&#8217;));<\/em><br \/>\n<em>});<\/em><\/p>\n<h2>Criando o rastreamento da p\u00e1gina<\/h2>\n<p>Agora que temos o servidor configurado para escutar conex\u00f5es de soquete de entrada vamos precisar servir ele com as informa\u00e7\u00f5es vindas do usu\u00e1rio.<\/p>\n<p>Em <strong>\/view\/index.html<\/strong> voc\u00ea ir\u00e1 precisar instanciar no arquivo a chamada do Soquet.IO, da seguinte forma:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13603 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img6.png\" alt=\"\" width=\"738\" height=\"295\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img6.png 738w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img6-300x120.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img6-400x160.png 400w\" sizes=\"(max-width: 738px) 100vw, 738px\" \/><\/p>\n<p>Ele ir\u00e1 apenas pegar os eventos do usu\u00e1rio.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13604 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img7.png\" alt=\"\" width=\"946\" height=\"431\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img7.png 946w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img7-300x137.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img7-768x350.png 768w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img7-780x355.png 780w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img7-400x182.png 400w\" sizes=\"(max-width: 946px) 100vw, 946px\" \/><\/p>\n<p><em>io.on(&#8216;connection&#8217;, function(socket) {<\/em><br \/>\n<em> if (socket.handshake.headers.host === config.host<\/em><br \/>\n<em> &amp;&amp; socket.handshake.headers.referer.indexOf(config.host + config.dashboardEndpoint) &gt; -1) {<\/em><br \/>\n<em> io.emit(&#8216;updated-stats&#8217;, computeStats());<\/em><br \/>\n<em> }<\/em><br \/>\n<em> socket.on(&#8216;visitor-data&#8217;, function(data) {<\/em><br \/>\n<em> visitorsData[socket.id] = data;<\/em><br \/>\n<em> io.emit(&#8216;updated-stats&#8217;, computeStats());<\/em><br \/>\n<em> });<\/em><br \/>\n<em> socket.on(&#8216;disconnect&#8217;, function() {<\/em><br \/>\n<em> delete visitorsData[socket.id];<\/em><\/p>\n<p>Agora, sempre que o lado do cliente emite um evento de visitor-data, adicionaremos os dados que capturamos no lado do cliente ao objeto visitorsData. Da mesma forma, quando eles saem da p\u00e1gina, o soquete desconectar\u00e1 e acionar\u00e1 um evento de desconex\u00e3o, no qual removeremos o ID de soquete associado e seus dados do objeto visitorData.<br \/>\nAgora temos um sistema no qual os dados do visitante s\u00e3o adicionados e removidos do objeto visitorsData, dependendo de eles visitarem a nossa p\u00e1gina ou a deixarem.<\/p>\n<p>No arquivo <strong>config.js<\/strong> h\u00e1 o host e o endpoint do dashboard:<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13605 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img8.png\" alt=\"\" width=\"542\" height=\"130\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img8.png 542w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img8-300x72.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img8-400x96.png 400w\" sizes=\"(max-width: 542px) 100vw, 542px\" \/><\/p>\n<p><em>module.exports = {<\/em><br \/>\n<em> host: &#8220;localhost:3000&#8221;,<\/em><br \/>\n<em> dashboardEndpoint: &#8220;\/dashboard&#8221;<\/em><br \/>\n<em>}<\/em><\/p>\n<p>Na instru\u00e7\u00e3o if do arquivo <strong>app.js<\/strong>, estamos verificando se <strong>socket.handshake.headers.host<\/strong> \u00e9 igual ao host que definimos em nosso arquivo de configura\u00e7\u00e3o e que <strong>socket.handshake.headers.referer<\/strong> cont\u00e9m <strong>localhost: 3000\/dashboard<\/strong>. Se a condi\u00e7\u00e3o for atendida, n\u00f3s enviaremos os dados para esse soquete, que \u00e9 o nosso soquete do painel neste caso.<\/p>\n<p>Isso simplesmente evita ter que calcular e enviar os dados toda vez que um novo soquete for criado, seja de nossos visitantes ou de nosso terminal do painel.\u00a0No arquivo <strong>realtime.tar.gz<\/strong> voc\u00ea pode ver o <strong>app.js<\/strong> conclu\u00eddo tal como todos os outros arquivos j\u00e1 configurados, restando apenas voc\u00ea instalar o <strong>init, express, socket.io<\/strong> e configurar a porta definida no servidor node.<\/p>\n<h2>Sobre o painel<\/h2>\n<p>Estamos agora na parte final, onde h\u00e1 as configura\u00e7\u00f5es do VueJS com o painel dashboard.html.<br \/>\nO arquivo est\u00e1 em \/views e \u00e9 muito simples como pode ver, tirando alguns minutos para analisar o c\u00f3digo voc\u00ea j\u00e1 ir\u00e1 ficar familiarizado.<br \/>\nO <strong>VueJs<\/strong> \u00e9 uma biblioteca JavaScript simples e flex\u00edvel que compartilha algumas semelhan\u00e7as com o Angular e o React.<br \/>\nO <strong>dashboard.js<\/strong> em <strong>public\/js<\/strong> conter\u00e1 tr\u00eas propriedades de dados: as p\u00e1ginas, os referenciadores e os usu\u00e1rios ativos, todos os quais o servidor calcula.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13606 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img9.png\" alt=\"\" width=\"698\" height=\"563\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img9.png 698w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img9-300x242.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img9-400x323.png 400w\" sizes=\"(max-width: 698px) 100vw, 698px\" \/><\/p>\n<p><em>var socket = io();<\/em><br \/>\n<em>var vm = new Vue({<\/em><br \/>\n<em> el: &#8216;#app&#8217;,<\/em><br \/>\n<em> data: {<\/em><br \/>\n<em> pages: {},<\/em><br \/>\n<em> referrers: {},<\/em><br \/>\n<em> activeUsers: 0<\/em><br \/>\n<em> },<\/em><br \/>\n<em> created: function() {<\/em><br \/>\n<em> socket.on(&#8216;updated-stats&#8217;, function(data) {<\/em><br \/>\n<em> this.pages = data.pages;<\/em><br \/>\n<em> this.referrers = data.referrers;<\/em><br \/>\n<em> this.activeUsers = data.activeUsers;<\/em><br \/>\n<em> }.bind(this));<\/em><br \/>\n<em> }<\/em><br \/>\n<em>});<\/em><\/p>\n<p>\u00c9 criando uma nova conex\u00e3o de soquete para o servidor na parte superior do arquivo e usando o new Vue() construtor new Vue() para criar uma nova inst\u00e2ncia do VueJS montada em <strong>#app<\/strong> .<\/p>\n<p>Agora, sempre que um evento de estat\u00edsticas atualizadas \u00e9 emitido pelo servidor (informando que um usu\u00e1rio foi adicionado\/removido), o manipulador de eventos definir\u00e1 as propriedades de dados pages, referrers e <strong>activeUsers<\/strong> para as estat\u00edsticas rec\u00e9m-calculadas recebidas do servidor.<\/p>\n<p>E por enquanto \u00e9 s\u00f3&#8230;<\/p>\n<p>rode o servidor \u201cnode app.js\u201d.<\/p>\n<p><strong>localhost:3000\/dashboard<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13607 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img10.png\" alt=\"\" width=\"949\" height=\"258\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img10.png 949w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img10-300x82.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img10-768x209.png 768w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img10-780x212.png 780w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img10-400x109.png 400w\" sizes=\"(max-width: 949px) 100vw, 949px\" \/><\/p>\n<p><strong>localhost:3000<\/strong><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13608 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img11.png\" alt=\"\" width=\"949\" height=\"198\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img11.png 949w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img11-300x63.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img11-768x160.png 768w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img11-780x163.png 780w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img11-400x83.png 400w\" sizes=\"(max-width: 949px) 100vw, 949px\" \/><\/p>\n<p>Conforme as p\u00e1ginas s\u00e3o carregadas, o dasboard \u00e9 atualizado instantaneamente.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13609 size-full\" src=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img12.png\" alt=\"\" width=\"947\" height=\"423\" title=\"\" srcset=\"https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img12.png 947w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img12-300x134.png 300w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img12-768x343.png 768w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img12-780x348.png 780w, https:\/\/cdn-cms.king.host\/blog-hlg\/uploads\/2018\/05\/img12-400x179.png 400w\" sizes=\"(max-width: 947px) 100vw, 947px\" \/><\/p>\n<h2>CONCLUS\u00c3O<\/h2>\n<p>Seguindo este tutorial, espero ter conseguido passar um primeiro passo para voc\u00ea seguir trabalhando com a tecnologia em tempo real com <strong>Node JS<\/strong> e <strong>Socket.IO<\/strong>.<br \/>\nAinda h\u00e1 muita coisa que pode ser abstra\u00edda, trabalhamos com um Javascript bem b\u00e1sico, mas <strong>um primeiro passo \u00e9 sempre importante para deixar a comunidade Node JS mais encorajada a seguir o trabalho<\/strong>. A KingHost possui planos de hospedagem de sites com suporte a Node.JS. <a href=\"https:\/\/king.host\/blog\/2016\/04\/hospedagem-node-js-agora-voce-sabe-onde-encontrar\/\" target=\"_blank\" rel=\"noopener\">Clique aqui para saber mais<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Node JS se tornou popular devido a sua facilidade em trabalhar com real-time e baixa performance do sistema, simplesmente por que o protocolo WebSockets utiliza o Javascript. Com WebSockets, um cliente e um servidor podem \u201cconversar\u201c entre si em tempo real, como se estivessem realizando uma chamada telef\u00f4nica. Uma vez conectado, um cliente poder\u00e1 receber [&hellip;]<\/p>\n","protected":false},"author":309,"featured_media":13613,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1179,1327,8],"tags":[1355],"class_list":["post-13596","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-desenvolvimento","category-hospedagem-node-js","category-tecnologia","tag-nodejs"],"_links":{"self":[{"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/posts\/13596","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/users\/309"}],"replies":[{"embeddable":true,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/comments?post=13596"}],"version-history":[{"count":13,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/posts\/13596\/revisions"}],"predecessor-version":[{"id":36113,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/posts\/13596\/revisions\/36113"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/media\/13613"}],"wp:attachment":[{"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/media?parent=13596"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/categories?post=13596"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/king.host\/blog\/wp-json\/wp\/v2\/tags?post=13596"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}